Example #1
0
/**@ingroup tnet_socket_group
 * @retval	Zero if succeed and nonzero error code otherwise.
 */
int tnet_socket_handle_brokenpipe(tnet_socket_t* self)
{
    int ret;
    tnet_fd_t fd_old, fd_new;
    if (!self || !TNET_SOCKET_TYPE_IS_DGRAM(self->type)) { // Must be UDP
        TSK_DEBUG_ERROR("Invalid parameter");
        return -1;
    }
    fd_old = self->fd;
    fd_new = TNET_INVALID_FD;
    
    // close old fd
    ret = tnet_sockfd_close(&self->fd);
    // try to create an fd binding to the same address
    if ((ret = tnet_sockfd_init(self->ip, self->port, self->type, &fd_new)) != 0) {
        TNET_PRINT_LAST_ERROR("Find to bind to %s:%d", self->ip, self->port);
        // TODO: Create completly new socket?
        return ret;
    }
#if TNET_UNDER_IPHONE || TNET_UNDER_IPHONE_SIMULATOR
    /* disable SIGPIPE signal */
    {
        int yes = 1;
        if (setsockopt(fd_new, SOL_SOCKET, SO_NOSIGPIPE, (char*)&yes, sizeof(int))){
            TNET_PRINT_LAST_ERROR("setsockopt(%d, SO_NOSIGPIPE) have failed", fd_new);
        }
    }
#endif /* TNET_UNDER_IPHONE || TNET_UNDER_IPHONE_SIMULATOR */
    TSK_DEBUG_INFO("Broken pipe result for {%s:%d}: %d -> %d", self->ip, self->port, fd_old, fd_new);
    self->fd = fd_new;
    return 0;
}
/*
* Sends dgarm to the specified destionation.
*/
tsk_size_t tnet_transport_sendto(const tnet_transport_handle_t *handle, tnet_fd_t from, const struct sockaddr *to, const void* buf, tsk_size_t size)
{
	tnet_transport_t *transport = (tnet_transport_t*)handle;
	WSABUF wsaBuffer;
	DWORD numberOfBytesSent = 0;
	int ret = -1;

	if (!transport){
		TSK_DEBUG_ERROR("Invalid server handle.");
		return ret;
	}

	if (!TNET_SOCKET_TYPE_IS_DGRAM(transport->master->type)){
		TSK_DEBUG_ERROR("In order to use WSASendTo you must use an udp transport.");
		return ret;
	}

	wsaBuffer.buf = (CHAR*)buf;
	wsaBuffer.len = (ULONG)size;

	if ((ret = WSASendTo(from, &wsaBuffer, 1, &numberOfBytesSent, 0, to, tnet_get_sockaddr_size(to), 0, 0)) == SOCKET_ERROR){
		if ((ret = WSAGetLastError()) == WSA_IO_PENDING){
			TSK_DEBUG_INFO("WSA_IO_PENDING error for WSASendTo SSESSION");
			ret = 0;
		}
		else{
			TNET_PRINT_LAST_ERROR("WSASendTo have failed.");
			return ret;
		}
	}
	else ret = 0;

	return numberOfBytesSent;
}
Example #3
0
tnet_dtls_socket_handle_t* tnet_dtls_socket_create(struct tnet_socket_s* wrapped_sock, struct ssl_ctx_st* ssl_ctx)
{
#if !HAVE_OPENSSL || !HAVE_OPENSSL_DTLS
	TSK_DEBUG_ERROR("OpenSSL or DTLS not enabled");
	return tsk_null;
#else
	tnet_dtls_socket_t* socket;

	if (!wrapped_sock || !ssl_ctx){
		TSK_DEBUG_ERROR("Invalid parameter");
		return tsk_null;
	}
	if ((socket = tsk_object_new(tnet_dtls_socket_def_t))) {
		const tsk_bool_t set_mtu = TNET_SOCKET_TYPE_IS_DGRAM(wrapped_sock->type) || 1; //!\ This is required even if the local transport is TCP/TLS because the relayed (TURN) transport could be UDP
		socket->wrapped_sock = tsk_object_ref(wrapped_sock);
		if (!(socket->ssl = SSL_new(ssl_ctx))) {
			TSK_DEBUG_ERROR("SSL_new(CTX) failed [%s]", ERR_error_string(ERR_get_error(), tsk_null));
			TSK_OBJECT_SAFE_FREE(socket);
			return tsk_null;
		}
		if (set_mtu) {
			SSL_set_options(socket->ssl, SSL_OP_NO_QUERY_MTU);
			SSL_set_mtu(socket->ssl, TNET_DTLS_MTU - 28);
			socket->ssl->d1->mtu = TNET_DTLS_MTU - 28;
		}
		if (!(socket->rbio = BIO_new(BIO_s_mem())) || !(socket->wbio = BIO_new(BIO_s_mem()))){
			TSK_DEBUG_ERROR("BIO_new_socket(%d) failed [%s]", socket->wrapped_sock->fd, ERR_error_string(ERR_get_error(), tsk_null));
			if (socket->rbio){
				BIO_free(socket->rbio);
			}
			if (socket->wbio){
				BIO_free(socket->wbio);
			}
			TSK_OBJECT_SAFE_FREE(socket);
			return tsk_null;
		}
		BIO_set_mem_eof_return(socket->rbio, -1);
		BIO_set_mem_eof_return(socket->wbio, -1);
		SSL_set_bio(socket->ssl, socket->rbio, socket->wbio);
		SSL_set_mode(socket->ssl, SSL_MODE_AUTO_RETRY);
		SSL_set_read_ahead(socket->ssl, 1);
		if (set_mtu) {
			BIO_ctrl(SSL_get_wbio(socket->ssl), BIO_CTRL_DGRAM_SET_MTU, TNET_DTLS_MTU - 28, NULL);
		}

		if ((socket->verify_peer = (SSL_CTX_get_verify_mode(ssl_ctx) != SSL_VERIFY_NONE))){
			TSK_DEBUG_INFO("SSL cert verify: ON");
			socket->verify_peer = tsk_true;
			SSL_set_verify(socket->ssl, (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT), _tnet_dtls_verify_cert);
		}
		else {
			TSK_DEBUG_ERROR("Verity not enabled");
		}

		SSL_set_app_data(socket->ssl, socket);
	}
	return socket;
#endif
}
Example #4
0
int tnet_transport_get_public_ip_n_port(const tnet_transport_handle_t *handle, tnet_fd_t fd, tnet_ip_t *ip, tnet_port_t *port)
{
	tsk_bool_t stun_ok = tsk_false;
	struct tnet_nat_ctx_s* natt_ctx;
	const tnet_transport_t *transport = handle;
	if(!transport){
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}

	if (TNET_SOCKET_TYPE_IS_DGRAM(transport->type) && (natt_ctx = tsk_object_ref(transport->natt_ctx))) {
		tnet_stun_binding_id_t bind_id = kStunBindingInvalidId;
		// if the socket is already monitored by the transport we should pause because both the transport and
		// NAT binder will try to read from it
		
		// Pause the soket
		tnet_transport_pause_socket(transport, fd, tsk_true);
		// Performs STUN binding
		bind_id = tnet_nat_stun_bind(transport->natt_ctx, fd);
		// Resume the socket
		tnet_transport_pause_socket(transport, fd, tsk_false);
		
		if (bind_id != kStunBindingInvalidId) {
			char* public_ip = tsk_null;
			if(tnet_nat_stun_get_reflexive_address(transport->natt_ctx, bind_id, &public_ip, port) == 0){
				if(ip && public_ip){
					tsk_size_t ip_len = tsk_strlen(public_ip);
					memcpy(ip, public_ip, ip_len> sizeof(*ip)?sizeof(*ip):ip_len);
				}
				stun_ok = tsk_true;
			}
			TSK_FREE(public_ip);
			tnet_nat_stun_unbind(transport->natt_ctx, bind_id);
		}
		tsk_object_unref(natt_ctx);
	}

	if(!stun_ok){
		if(fd == TNET_INVALID_FD && transport->local_ip){
			memcpy(*ip, transport->local_ip, TSK_MIN(sizeof(tnet_ip_t), tsk_strlen(transport->local_ip)));
			*port = transport->bind_local_port;
			return 0;
		}
		else{
			return tnet_transport_get_ip_n_port(handle, fd, ip, port);
		}
	}
	
	return 0;
}
tsk_size_t tnet_transport_sendto(const tnet_transport_handle_t *handle, tnet_fd_t from, const struct sockaddr *to, const void* buf, tsk_size_t size)
{
	tnet_transport_t *transport = (tnet_transport_t*)handle;
	int numberOfBytesSent = 0;
	
	if (!transport) {
		TSK_DEBUG_ERROR("Invalid server handle.");
		goto bail;
	}
	
	if (!TNET_SOCKET_TYPE_IS_DGRAM(transport->master->type)) {
		TSK_DEBUG_ERROR("In order to use sendto you must use an udp transport.");
		goto bail;
	}
	
    if ((numberOfBytesSent = sendto(from, buf, size, 0, to, sizeof(*to))) <= 0) {
		TNET_PRINT_LAST_ERROR("sendto have failed.");
		goto bail;
	}
    
bail:
	return numberOfBytesSent;
}
Example #6
0
/**@ingroup tnet_nat_group
 * 	Internal function to send a STUN2 binding request over the network.
 *
 * @param [in,out]	p_self	The NAT context holding the user preferences.
 * @param [in,out]	p_binding	The STUN binding object used to create the message to send.
 *
 * @return	Zero if succeed and non-zero error code otherwise.
**/
int tnet_nat_stun_send_bind(const struct tnet_nat_ctx_s* pc_self, struct tnet_stun_binding_s *p_binding)
{
    int ret = -1;
    tnet_stun_pkt_resp_t *p_pkt_resp = tsk_null;
    tnet_stun_pkt_req_t *p_pkt_req = tsk_null;

    if (!pc_self || !p_binding) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return -1;
    }

    if (!TNET_SOCKET_TYPE_IS_DGRAM(p_binding->socket_type)) {
        TSK_DEBUG_ERROR("Only DGRAM could be used for STUN transport");
        return -2;
    }

    if ((ret = tnet_stun_binding_create_req(p_binding, &p_pkt_req))) {
        goto bail;
    }

    /*	RFC 5389 - 10.2.1.1.  First Request
    	If the client has not completed a successful request/response
    	transaction with the server (as identified by hostname, if the DNS
    	procedures of Section 9 are used, else IP address if not), it SHOULD
    	omit the USERNAME, MESSAGE-INTEGRITY, REALM, and NONCE attributes.
    	In other words, the very first request is sent as if there were no
    	authentication or message integrity applied.
    */
stun_phase0: {
        if ((ret = tnet_stun_utils_send_unreliably(p_binding->localFD, pc_self->RTO, pc_self->Rc, p_pkt_req, (struct sockaddr*)&p_binding->addr_server, &p_pkt_resp))) {
            goto bail;
        }

        if (p_pkt_resp) {
            if (TNET_STUN_PKT_RESP_IS_ERROR(p_pkt_resp)) {
                uint16_t u_code;
                if ((ret = tnet_stun_pkt_get_errorcode(p_pkt_resp, &u_code))) {
                    goto bail;
                }
                if (u_code == kStunErrCodeUnauthorized || u_code == kStunErrCodeStaleNonce) {
                    if (u_code == kStunErrCodeUnauthorized) {
                        // Make sure this is not an authentication failure (#2 401)
                        // Do not send another req to avoid endless messages
                        if ((tnet_stun_pkt_attr_exists(p_pkt_req, tnet_stun_attr_type_message_integrity))) { // already has a MESSAGE-INTEGRITY?
                            TSK_DEBUG_ERROR("STUN authentication failed");
                            goto bail;
                        }
                    }
                    if ((ret = tnet_stun_pkt_auth_prepare_2(p_pkt_req, p_binding->p_username, p_binding->p_password, p_pkt_resp))) {
                        goto bail;
                    }
                    // Try to send again now that authinfo is up2date
                    goto stun_phase0;
                }
                else if (u_code == kStunErrCodeUnknownAttributes) {
                    if((ret = tnet_stun_pkt_process_err420(p_pkt_req, p_pkt_resp))) {
                        goto bail;
                    }
                    // Try to send again now that authinfo is up2date
                    goto stun_phase0;
                }
                ret = -3;
            }
            else {
                const tnet_stun_attr_address_t* pc_addr;
                if ((ret = tnet_stun_pkt_attr_find_first(p_pkt_resp, tnet_stun_attr_type_xor_mapped_address, (const tnet_stun_attr_t**)&pc_addr)) == 0 && pc_addr) {
                    TSK_OBJECT_SAFE_FREE(p_binding->p_xmaddr);
                    p_binding->p_xmaddr = tsk_object_ref(TSK_OBJECT(pc_addr));
                }
                if ((ret = tnet_stun_pkt_attr_find_first(p_pkt_resp, tnet_stun_attr_type_mapped_address, (const tnet_stun_attr_t**)&pc_addr)) == 0 && pc_addr) {
                    TSK_OBJECT_SAFE_FREE(p_binding->p_maddr);
                    p_binding->p_maddr = tsk_object_ref(TSK_OBJECT(pc_addr));
                }
            }
        }
    }
    /* END OF stun_phase0 */

bail:
    TSK_OBJECT_SAFE_FREE(p_pkt_resp);
    TSK_OBJECT_SAFE_FREE(p_pkt_req);

    return ret;
}
Example #7
0
/**
* Connects a socket.
* @param handle The transport to use to connect() the socket. The new socket will be managed by this transport.
* @param host The remote @a host to connect() to.
* @param port The remote @a port to connect() to.
* @param type The type of the socket to use to connect() to the remote @a host.
* @retval The newly connected socket. For non-blocking sockets you should use @ref tnet_sockfd_waitUntilWritable to check
* the socket for writability.
* @sa tnet_sockfd_waitUntilWritable.
*/
tnet_fd_t tnet_transport_connectto(const tnet_transport_handle_t *handle, const char* host, tnet_port_t port, tnet_socket_type_t type)
{
	tnet_transport_t *transport = (tnet_transport_t*)handle;
	struct sockaddr_storage to;
	int status = -1;
	tnet_fd_t fd = TNET_INVALID_FD;
	tnet_tls_socket_handle_t* tls_handle = tsk_null;
	
	if(!transport || !transport->master){
		TSK_DEBUG_ERROR("Invalid transport handle");
		goto bail;
	}
	
	if((TNET_SOCKET_TYPE_IS_STREAM(transport->master->type) && !TNET_SOCKET_TYPE_IS_STREAM(type)) ||
		(TNET_SOCKET_TYPE_IS_DGRAM(transport->master->type) && !TNET_SOCKET_TYPE_IS_DGRAM(type))){
		TSK_DEBUG_ERROR("Master/destination types mismatch [%u/%u]", transport->master->type, type);
		goto bail;
	}

	/* Init destination sockaddr fields */
	if((status = tnet_sockaddr_init(host, port, type, &to))){
		TSK_DEBUG_ERROR("Invalid HOST/PORT [%s/%u]", host, port);
		goto bail;
	}
	else if(TNET_SOCKET_TYPE_IS_IPV46(type)){
		/* Update the type (unambiguously) */
		if(to.ss_family == AF_INET6){
			TNET_SOCKET_TYPE_SET_IPV6Only(type);
		}
		else{
			TNET_SOCKET_TYPE_SET_IPV4Only(type);
		}
	}
	
	/*
	* STREAM ==> create new socket and connect it to the remote host.
	* DGRAM ==> connect the master to the remote host.
	*/
	if(TNET_SOCKET_TYPE_IS_STREAM(type)){		
		/* Create client socket descriptor. */
		if((status = tnet_sockfd_init(transport->local_host, TNET_SOCKET_PORT_ANY, type, &fd))){
			TSK_DEBUG_ERROR("Failed to create new sockfd.");
			goto bail;
		}
	}
	else{
		fd = transport->master->fd;
	}
	
	if((status = tnet_sockfd_connectto(fd, (const struct sockaddr_storage *)&to))){
		if(fd != transport->master->fd){
			tnet_sockfd_close(&fd);
		}
		goto bail;
	}
	else{
        static const tsk_bool_t __isClient = tsk_true;
        static const tsk_bool_t __takeOwnership = tsk_true;
		if(TNET_SOCKET_TYPE_IS_TLS(type) || TNET_SOCKET_TYPE_IS_WSS(type)){
#if HAVE_OPENSSL
			tls_handle = tnet_tls_socket_create(fd, transport->tls.ctx_client);     
			if((status = tnet_tls_socket_connect(tls_handle))){
				tnet_sockfd_close(&fd);
				goto bail;
			}
#endif
		}
        /* Add the socket */
        // socket must be added after connect() otherwise many Linux systems when return POLLHUP as the fd is not active yet
        if((status = tnet_transport_add_socket(handle, fd, type, __takeOwnership, __isClient, tls_handle))){
            TNET_PRINT_LAST_ERROR("Failed to add new socket");
            tnet_sockfd_close(&fd);
            goto bail;
        }
	}
	
bail:
	TSK_OBJECT_SAFE_FREE(tls_handle);
	return fd;
}
Example #8
0
int tnet_dtls_socket_do_handshake(tnet_dtls_socket_handle_t* handle, const struct sockaddr_storage* remote_addr)
{
#if !HAVE_OPENSSL || !HAVE_OPENSSL_DTLS
	TSK_DEBUG_ERROR("OpenSSL or DTLS not enabled");
	return -1;

#else
	tnet_dtls_socket_t *socket = handle;
	int ret = 0, len;
	void* out_data;

	if (!socket) {
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}

	tsk_safeobj_lock(socket);

	// update remote address even if handshaking is completed
	if (remote_addr) {
		socket->remote.addr = *remote_addr;
	}

	if (socket->handshake_completed) {
		TSK_DEBUG_INFO("Handshake completed");
		ret = 0;
		goto bail;
	}

	if (!socket->handshake_started) {
		if ((ret = SSL_do_handshake(socket->ssl)) != 1) {
			switch ((ret = SSL_get_error(socket->ssl, ret))) {
			case SSL_ERROR_WANT_READ:
			case SSL_ERROR_WANT_WRITE:
			case SSL_ERROR_NONE:
				break;
			default:
				TSK_DEBUG_ERROR("DTLS handshake failed [%s]", ERR_error_string(ERR_get_error(), tsk_null));
				_tnet_dtls_socket_raise_event_dataless(socket, tnet_dtls_socket_event_type_handshake_failed);
				ret = -2;
				goto bail;
			}
		}
		socket->handshake_started = (ret == SSL_ERROR_NONE); // TODO: reset for renegotiation
	}

	if ((len = (int)BIO_get_mem_data(socket->wbio, &out_data)) > 0 && out_data) {
		if (socket->handshake_storedata) { // e.g. when TURN is enabled we have to query handshaking data and sent it via the negotiated channel
			if ((int)socket->handshake_data.size < len) {
				if (!(socket->handshake_data.ptr = tsk_realloc(socket->handshake_data.ptr, len))) {
					socket->handshake_data.size = 0;
					socket->handshake_data.count = 0;
					ret = -5;
					goto bail;
				}
				socket->handshake_data.size = len;
			}
			socket->handshake_data.count = len;
			memcpy(socket->handshake_data.ptr, out_data, len);
		}
		else {
			int sentlen = 0;
			tnet_port_t port;
			tnet_ip_t ip;
			tsk_bool_t is_dgram = TNET_SOCKET_TYPE_IS_DGRAM(socket->wrapped_sock->type);
			const uint8_t *record_ptr, *records_ptr = out_data;
			tsk_size_t record_size;
			int records_len = len;

			tnet_get_sockip_n_port((const struct sockaddr *)&socket->remote.addr, &ip, &port);
			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) {
				if (is_dgram) {
					sentlen += tnet_sockfd_sendto(socket->wrapped_sock->fd, (const struct sockaddr *)&socket->remote.addr, record_ptr, record_size);
				}
				else {
					sentlen += tnet_socket_send_stream(socket->wrapped_sock, record_ptr, record_size);
				}
				records_len -= (int)record_size;
				records_ptr += record_size;
			}
			TSK_DEBUG_INFO("DTLS data handshake sent len = %d", sentlen);
		}
	}

	BIO_reset(socket->rbio);
	BIO_reset(socket->wbio);

	if ((socket->handshake_completed = SSL_is_init_finished(socket->ssl))) {
		TSK_DEBUG_INFO("DTLS handshake completed");

#if HAVE_OPENSSL_DTLS_SRTP
		if (socket->use_srtp){
#if !defined(SRTP_MAX_KEY_LEN)
#	define cipher_key_length (128 >> 3) // rfc5764 4.1.2.  SRTP Protection Profiles
#	define cipher_salt_length (112 >> 3) // rfc5764 4.1.2.  SRTP Protection Profiles
			// "cipher_key_length" is also equal to srtp_profile_get_master_key_length(srtp_profile_aes128_cm_sha1_80)
			// "cipher_salt_length" is also srtp_profile_get_master_salt_length(srtp_profile_aes128_cm_sha1_80)
#	define SRTP_MAX_KEY_LEN (cipher_key_length + cipher_salt_length)
#endif /* SRTP_MAX_KEY_LEN */
#define EXTRACTOR_dtls_srtp_text "EXTRACTOR-dtls_srtp"
#define EXTRACTOR_dtls_srtp_text_len 19
			uint8_t keying_material[SRTP_MAX_KEY_LEN << 1];
			static const tsk_size_t keying_material_size = sizeof(keying_material);
			/*if(socket->use_srtp)*/{
				SRTP_PROTECTION_PROFILE *p = SSL_get_selected_srtp_profile(socket->ssl);
				if (!p) {
					TSK_DEBUG_ERROR("SSL_get_selected_srtp_profile() returned null [%s]", ERR_error_string(ERR_get_error(), tsk_null));
					ret = -2;
					goto bail;
				}
				// alert user
				_tnet_dtls_socket_raise_event(socket, tnet_dtls_socket_event_type_dtls_srtp_profile_selected, p->name, tsk_strlen(p->name));

				memset(keying_material, 0, sizeof(keying_material));

				// rfc5764 - 4.2.  Key Derivation
				ret = SSL_export_keying_material(socket->ssl, keying_material, sizeof(keying_material), EXTRACTOR_dtls_srtp_text, EXTRACTOR_dtls_srtp_text_len, tsk_null, 0, 0);
				if (ret != 1) {
					// alert listener
					_tnet_dtls_socket_raise_event_dataless(socket, tnet_dtls_socket_event_type_error);
					TSK_DEBUG_ERROR("SSL_export_keying_material() failed [%s]", ERR_error_string(ERR_get_error(), tsk_null));
					ret = -2;
					goto bail;
				}
			}
			// alert listener
			_tnet_dtls_socket_raise_event(socket, tnet_dtls_socket_event_type_dtls_srtp_data, keying_material, keying_material_size);
		}
#endif /* HAVE_OPENSSL_DTLS_SRTP */
		_tnet_dtls_socket_raise_event_dataless(socket, tnet_dtls_socket_event_type_handshake_succeed);
	}
	ret = 0; // clear "ret", error will directly jump to "bail:"
bail:
	tsk_safeobj_unlock(socket);
	return ret;
#endif
}
Example #9
0
tnet_fd_t tnet_transport_connectto_3(const tnet_transport_handle_t *handle, struct tnet_socket_s* socket, const char* host, tnet_port_t port, tnet_socket_type_t type)
{
    tnet_transport_t *transport = (tnet_transport_t*)handle;
    struct sockaddr_storage to;
    int status = -1;
    tnet_fd_t fd = socket ? socket->fd : TNET_INVALID_FD;
    tnet_tls_socket_handle_t* tls_handle = tsk_null;
    tsk_bool_t owe_socket = socket ? tsk_false : tsk_true;
    tsk_bool_t use_proxy = TNET_SOCKET_TYPE_IS_STREAM(type);
    const char* to_host = host;
    tnet_port_t to_port = port;
    tnet_socket_type_t to_type = type;
    tnet_proxyinfo_t* proxy_info = tsk_null;
    
    if (!transport || !transport->master) {
        TSK_DEBUG_ERROR("Invalid transport handle");
        goto bail;
    }
    
    if ((TNET_SOCKET_TYPE_IS_STREAM(transport->master->type) && !TNET_SOCKET_TYPE_IS_STREAM(type)) ||
        (TNET_SOCKET_TYPE_IS_DGRAM(transport->master->type) && !TNET_SOCKET_TYPE_IS_DGRAM(type))) {
        TSK_DEBUG_ERROR("Master/destination types mismatch [%u/%u]", transport->master->type, type);
        goto bail;
    }
    
    if (use_proxy) {
        // auto-detect the proxy
        if (transport->proxy.auto_detect) {
            char* url = tsk_null;
            // The proxy detection implementations are designed for a browser and expect a "http://" or "https://" schemes (will work with socks).
            tsk_sprintf(&url, "%s://%s:%d", TNET_SOCKET_TYPE_IS_TLS(to_type) ? "https" : "http", to_host, to_port);
            proxy_info = tnet_proxydetect_get_info_fast(url, to_type);
            TSK_FREE(url);
        }
        // fall-back to the hard proxy if auto-detection failed
        if (!tnet_proxyinfo_is_valid(proxy_info) && tnet_proxyinfo_is_valid(transport->proxy.info)) {
            proxy_info = tsk_object_ref(transport->proxy.info);
        }
    }
    
    use_proxy &= tnet_proxyinfo_is_valid(proxy_info);
    if (use_proxy) {
        if (tnet_proxy_node_is_nettransport_supported(proxy_info->type, type)) {
            to_host = proxy_info->hostname;
            to_port = proxy_info->port;
            // SOCKS still doesn't define RFC for SSL security (https://tools.ietf.org/html/draft-ietf-aft-socks-ssl-00) but Kerberos6 authentication is supported
            if (proxy_info->type == tnet_proxy_type_http || proxy_info->type == tnet_proxy_type_socks4 || proxy_info->type == tnet_proxy_type_socks4a || proxy_info->type == tnet_proxy_type_socks5) {
                // Send CONNET to the proxy using unsecure connection then begin SSL handshaking if needed
                TNET_SOCKET_TYPE_UNSET(to_type, TLS); // Make the type unsecure (will keep other flags-e.g. IP version-)
                TNET_SOCKET_TYPE_SET(to_type, TCP); // Use plain TCP
            }
        }
        else {
            // Not an error.
            TSK_DEBUG_INFO("No proxy plugin to handle network transport type = %d", type);
            use_proxy = tsk_false;
        }
    }
    
    TSK_DEBUG_INFO("tnet_transport_connectto_3(host=%s, port=%d, type=%d, fd=%d, use_proxy=%d, to_host=%s, to_port=%d, to_type=%d, proxy_type=%d)" , host, port, type, fd, use_proxy, to_host, to_port, to_type, proxy_info ? proxy_info->type : 0);
    
    /* Init destination sockaddr fields */
    if ((status = tnet_sockaddr_init(to_host, to_port, to_type, &to))) {
        TSK_DEBUG_ERROR("Invalid HOST/PORT [%s/%u]", host, port);
        goto bail;
    }
    if (TNET_SOCKET_TYPE_IS_IPV46(type)) {
        /* Update the type (unambiguously) */
        if (to.ss_family == AF_INET6) {
            TNET_SOCKET_TYPE_SET_IPV6Only(type);
        }
        else {
            TNET_SOCKET_TYPE_SET_IPV4Only(type);
        }
    }
    
    /*
     * STREAM ==> create new socket and connect it to the remote host.
     * DGRAM ==> connect the master to the remote host.
     */
    if (fd == TNET_INVALID_FD) {
        // Create client socket descriptor.
        if ((status = tnet_sockfd_init(transport->local_host, TNET_SOCKET_PORT_ANY, to_type, &fd))) {
            TSK_DEBUG_ERROR("Failed to create new sockfd.");
            goto bail;
        }
    }
    
    if ((status = tnet_sockfd_connectto(fd, (const struct sockaddr_storage *)&to))) {
        if (fd != transport->master->fd) {
            tnet_sockfd_close(&fd);
        }
        goto bail;
    }
    else {
        static const tsk_bool_t __isClient = tsk_true;
        if (TNET_SOCKET_TYPE_IS_TLS(to_type) || TNET_SOCKET_TYPE_IS_WSS(to_type)) {
#if HAVE_OPENSSL
            tls_handle = tnet_tls_socket_create(fd, transport->tls.ctx_client);
            if (socket) {
                TSK_OBJECT_SAFE_FREE(socket->tlshandle);
                socket->tlshandle = tsk_object_ref(tls_handle);
            }
            if ((status = tnet_tls_socket_connect(tls_handle))) {
                tnet_sockfd_close(&fd);
                goto bail;
            }
#endif
        }
        /* Add the socket */
        // socket must be added after connect() otherwise many Linux systems will return POLLHUP as the fd is not active yet
        if ((status = tnet_transport_add_socket_2(handle, fd, to_type, owe_socket, __isClient, tls_handle, host, port, proxy_info))) {
            TNET_PRINT_LAST_ERROR("Failed to add new socket");
            tnet_sockfd_close(&fd);
            goto bail;
        }
    }
    
bail:
    TSK_OBJECT_SAFE_FREE(tls_handle);
    TSK_OBJECT_SAFE_FREE(proxy_info);
    return status == 0 ? fd : TNET_INVALID_FD;
}
int tnet_transport_wrap(tnet_transport_t *transport, int index) {
    transport_context_t *context = transport->context;
    transport_socket_t *sock = context->sockets[index];
    
    // If the socket is already wrapped in a CFSocket then return.
    if (sock->cf_socket || sock->cf_read_stream) {
        return 1;
    }
    
    // Put a reference to the transport context 
    const CFSocketContext socket_context = { 0, transport, NULL, NULL, NULL };
    
    if (TNET_SOCKET_TYPE_IS_DGRAM(sock->type)) {
        
        // Create a CFSocket from the native socket and register for Read events
        sock->cf_socket = CFSocketCreateWithNative(kCFAllocatorDefault, 
                                                   sock->fd,
                                                   kCFSocketReadCallBack, 
                                                   &__CFSocketCallBack, 
                                                   &socket_context);
        
        // Don't close the socket if the CFSocket is invalidated
        CFOptionFlags flags = CFSocketGetSocketFlags(sock->cf_socket);
        flags = flags & ~kCFSocketCloseOnInvalidate;
        CFSocketSetSocketFlags(sock->cf_socket, flags);
		
        
        // Create a new RunLoopSource and register it with the main thread RunLoop
        sock->cf_run_loop_source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, sock->cf_socket, 0);
        CFRunLoopAddSource(context->cf_run_loop, sock->cf_run_loop_source, kCFRunLoopDefaultMode);
        CFRelease(sock->cf_run_loop_source);
        
    } else if (TNET_SOCKET_TYPE_IS_STREAM(sock->type)) {
        
        // Create a pair of streams (read/write) from the socket
        CFStreamCreatePairWithSocket(kCFAllocatorDefault, sock->fd, &sock->cf_read_stream, &sock->cf_write_stream);
        
        // Don't close underlying socket
        CFReadStreamSetProperty(sock->cf_read_stream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse);
        CFWriteStreamSetProperty(sock->cf_write_stream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse);
        
        if (TNET_SOCKET_TYPE_IS_SECURE(sock->type)) {
            CFMutableDictionaryRef settings = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
            CFDictionaryAddValue(settings, kCFStreamSSLAllowsExpiredCertificates, kCFBooleanTrue);
            CFDictionaryAddValue(settings, kCFStreamSSLAllowsAnyRoot, kCFBooleanTrue);
            CFDictionaryAddValue(settings, kCFStreamSSLValidatesCertificateChain, kCFBooleanFalse);
            CFDictionaryAddValue(settings, kCFStreamSSLPeerName, kCFNull);
            
            // Set the SSL settings
            CFReadStreamSetProperty(sock->cf_read_stream, kCFStreamPropertySocketSecurityLevel, kCFStreamSocketSecurityLevelNegotiatedSSL);
            CFReadStreamSetProperty(sock->cf_read_stream, kCFStreamPropertySSLSettings, settings);
            CFWriteStreamSetProperty(sock->cf_write_stream, kCFStreamPropertySocketSecurityLevel, kCFStreamSocketSecurityLevelNegotiatedSSL);
            CFWriteStreamSetProperty(sock->cf_write_stream, kCFStreamPropertySSLSettings, settings);
            
            CFRelease(settings);
        }
        
#if __IPHONE_4_0
        // Mark the stream for VoIP usage
        CFReadStreamSetProperty(sock->cf_read_stream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
        CFWriteStreamSetProperty(sock->cf_write_stream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
#endif
        
        // Setup a context for the streams
        CFStreamClientContext streamContext = { 0, transport, NULL, NULL, NULL };
        
        // Set the client callback for the stream
        CFReadStreamSetClient(sock->cf_read_stream, 
                              kCFStreamEventOpenCompleted | kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
                              &__CFReadStreamClientCallBack, 
                              &streamContext);
        CFWriteStreamSetClient(sock->cf_write_stream, 
                               kCFStreamEventOpenCompleted | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, 
                               &__CFWriteStreamClientCallBack, 
                               &streamContext);
        
        // Enroll streams in the run-loop
        CFReadStreamScheduleWithRunLoop(sock->cf_read_stream, context->cf_run_loop, kCFRunLoopDefaultMode);
        CFWriteStreamScheduleWithRunLoop(sock->cf_write_stream, context->cf_run_loop, kCFRunLoopDefaultMode);
        
        // Release references
        CFRelease(sock->cf_read_stream);
        CFRelease(sock->cf_write_stream);
        
        CFReadStreamOpen(sock->cf_read_stream);
        CFWriteStreamOpen(sock->cf_write_stream);
    }
    
    return 0;
}
/*=== Main thread */
void* TSK_STDCALL tnet_transport_mainthread(void *param)
{
    tnet_transport_t *transport = (tnet_transport_t*)param;
    transport_context_t *context = (transport_context_t*)transport->context;
    DWORD evt;
    WSANETWORKEVENTS networkEvents;
    DWORD flags = 0;
    int ret;

    struct sockaddr_storage remote_addr = { 0 };
    WSAEVENT active_event;
    transport_socket_xt* active_socket;
    int index;

    TSK_DEBUG_INFO("Starting [%s] server with IP {%s} on port {%d} with type {%d}...", transport->description, transport->master->ip, transport->master->port, transport->master->type);

    while (TSK_RUNNABLE(transport)->running || TSK_RUNNABLE(transport)->started) {
        /* Wait for multiple events */
        if ((evt = WSAWaitForMultipleEvents((DWORD)context->count, context->events, FALSE, WSA_INFINITE, FALSE)) == WSA_WAIT_FAILED) {
            TNET_PRINT_LAST_ERROR("WSAWaitForMultipleEvents have failed.");
            goto bail;
        }

        if (!TSK_RUNNABLE(transport)->running && !TSK_RUNNABLE(transport)->started) {
            goto bail;
        }

        /* lock context */
        tsk_safeobj_lock(context);

        /* Get active event and socket */
        index = (evt - WSA_WAIT_EVENT_0);
        active_event = context->events[index];
        if (!(active_socket = context->sockets[index])) {
            goto done;
        }

        /* Get the network events flags */
        if (WSAEnumNetworkEvents(active_socket->fd, active_event, &networkEvents) == SOCKET_ERROR) {
            TSK_RUNNABLE_ENQUEUE(transport, event_error, transport->callback_data, active_socket->fd);
            TNET_PRINT_LAST_ERROR("WSAEnumNetworkEvents have failed.");

            tsk_safeobj_unlock(context);
            goto bail;
        }

        /*================== FD_ACCEPT ==================*/
        if (networkEvents.lNetworkEvents & FD_ACCEPT) {
            tnet_fd_t fd;

            TSK_DEBUG_INFO("NETWORK EVENT FOR SERVER [%s] -- FD_ACCEPT", transport->description);

            if (networkEvents.iErrorCode[FD_ACCEPT_BIT]) {
                TSK_RUNNABLE_ENQUEUE(transport, event_error, transport->callback_data, active_socket->fd);
                TNET_PRINT_LAST_ERROR("ACCEPT FAILED.");
                goto done;
            }

            /* Accept the connection */
            if ((fd = (tnet_fd_t)WSAAccept(active_socket->fd, NULL, NULL, AcceptCondFunc, (DWORD_PTR)context)) != INVALID_SOCKET) {
                /* Add the new fd to the server context */
                addSocket(fd, transport->master->type, transport, tsk_true, tsk_false, tsk_null);
                if (active_socket->tlshandle) {
                    transport_socket_xt* tls_socket;
                    if ((tls_socket = getSocket(context, fd))) {
                        if (tnet_tls_socket_accept(tls_socket->tlshandle)) {
                            tnet_transport_remove_socket(transport, &fd);
                            TNET_PRINT_LAST_ERROR("SSL_accept() failed");
                            goto done;
                        }
                    }
                }
                if (WSAEventSelect(fd, context->events[context->count - 1], FD_READ | FD_WRITE | FD_CLOSE) == SOCKET_ERROR) {
                    tnet_transport_remove_socket(transport, &fd);
                    TNET_PRINT_LAST_ERROR("WSAEventSelect() have failed.");
                    goto done;
                }
                TSK_RUNNABLE_ENQUEUE(transport, event_accepted, transport->callback_data, fd);
            }
            else {
                TNET_PRINT_LAST_ERROR("ACCEPT FAILED.");
                goto done;
            }




        }

        /*================== FD_CONNECT ==================*/
        if (networkEvents.lNetworkEvents & FD_CONNECT) {
            TSK_DEBUG_INFO("NETWORK EVENT FOR SERVER [%s] -- FD_CONNECT", transport->description);

            if (networkEvents.iErrorCode[FD_CONNECT_BIT]) {
                tnet_fd_t fd = active_socket->fd;
                TSK_RUNNABLE_ENQUEUE(transport, event_error, transport->callback_data, fd);
                tnet_transport_remove_socket(transport, &fd);
                TNET_PRINT_LAST_ERROR("CONNECT FAILED.");
                goto done;
            }
            else {
                TSK_RUNNABLE_ENQUEUE(transport, event_connected, transport->callback_data, active_socket->fd);
                active_socket->connected = 1;
            }
        }


        /*================== FD_READ ==================*/
        if (networkEvents.lNetworkEvents & FD_READ) {
            DWORD readCount = 0;
            WSABUF wsaBuffer;

            /* TSK_DEBUG_INFO("NETWORK EVENT FOR SERVER [%s] -- FD_READ", transport->description); */

            /* check whether the socket is paused or not */
            if (active_socket->paused) {
                TSK_DEBUG_INFO("Socket is paused");
                goto FD_READ_DONE;
            }

            if (networkEvents.iErrorCode[FD_READ_BIT]) {
                TSK_RUNNABLE_ENQUEUE(transport, event_error, transport->callback_data, active_socket->fd);
                TNET_PRINT_LAST_ERROR("READ FAILED.");
                goto done;
            }

            /* Retrieve the amount of pending data */
            if (tnet_ioctlt(active_socket->fd, FIONREAD, &(wsaBuffer.len)) < 0) {
                TNET_PRINT_LAST_ERROR("IOCTLT FAILED.");
                goto done;
            }

            if (!wsaBuffer.len) {
                goto done;
            }

            /* Alloc data */
            if (!(wsaBuffer.buf = tsk_calloc(wsaBuffer.len, sizeof(uint8_t)))) {
                goto done;
            }

            /* Retrieve the remote address */
            if (TNET_SOCKET_TYPE_IS_STREAM(transport->master->type)) {
                ret = tnet_getpeername(active_socket->fd, &remote_addr);
            }

            /* Receive the waiting data. */
            if (active_socket->tlshandle) {
                int isEncrypted;
                tsk_size_t len = wsaBuffer.len;
                if (!(ret = tnet_tls_socket_recv(active_socket->tlshandle, &wsaBuffer.buf, &len, &isEncrypted))) {
                    if (isEncrypted) {
                        TSK_FREE(wsaBuffer.buf);
                        goto done;
                    }
                    wsaBuffer.len = (ULONG)len;
                }
            }
            else {
                if (TNET_SOCKET_TYPE_IS_STREAM(transport->master->type)) {
                    ret = WSARecv(active_socket->fd, &wsaBuffer, 1, &readCount, &flags, 0, 0);
                }
                else {
                    int len = sizeof(remote_addr);
                    ret = WSARecvFrom(active_socket->fd, &wsaBuffer, 1, &readCount, &flags,
                                      (struct sockaddr*)&remote_addr, &len, 0, 0);
                }
                if (readCount < wsaBuffer.len) {
                    wsaBuffer.len = readCount;
                    /* wsaBuffer.buf = tsk_realloc(wsaBuffer.buf, readCount); */
                }
            }

            if (ret) {
                ret = WSAGetLastError();
                if (ret == WSAEWOULDBLOCK) {
                    // Doesn't (always) mean congestion but... another thread is also poll()ing the FD. For example, when TURN session has a reference to the fd.
                    TSK_DEBUG_WARN("WSAEWOULDBLOCK error for READ SSESSION");
                }
                else if (ret == WSAECONNRESET && TNET_SOCKET_TYPE_IS_DGRAM(transport->master->type)) {
                    /* For DGRAM ==> The sent packet gernerated "ICMP Destination/Port unreachable" result. */
                    TSK_FREE(wsaBuffer.buf);
                    goto done; // ignore and retry.
                }
                else {
                    TSK_FREE(wsaBuffer.buf);

                    removeSocket(index, context);
                    TNET_PRINT_LAST_ERROR("WSARecv have failed.");
                    goto done;
                }
            }
            else {
                tnet_transport_event_t* e = tnet_transport_event_create(event_data, transport->callback_data, active_socket->fd);
                transport->bytes_in += wsaBuffer.len;
                e->data = wsaBuffer.buf;
                e->size = wsaBuffer.len;
                e->remote_addr = remote_addr;

                TSK_RUNNABLE_ENQUEUE_OBJECT_SAFE(TSK_RUNNABLE(transport), e);
            }
FD_READ_DONE:
            ;
        }




        /*================== FD_WRITE ==================*/
        if (networkEvents.lNetworkEvents & FD_WRITE) {
            TSK_DEBUG_INFO("NETWORK EVENT FOR SERVER [%s] -- FD_WRITE", transport->description);

            if (networkEvents.iErrorCode[FD_WRITE_BIT]) {
                TSK_RUNNABLE_ENQUEUE(transport, event_error, transport->callback_data, active_socket->fd);
                TNET_PRINT_LAST_ERROR("WRITE FAILED.");
                goto done;
            }
        }



        /*================== FD_CLOSE ==================*/
        if (networkEvents.lNetworkEvents & FD_CLOSE) {
            TSK_DEBUG_INFO("NETWORK EVENT FOR SERVER [%s] -- FD_CLOSE", transport->description);

            TSK_RUNNABLE_ENQUEUE(transport, event_closed, transport->callback_data, active_socket->fd);
            removeSocket(index, context);
        }

        /*	http://msdn.microsoft.com/en-us/library/ms741690(VS.85).aspx

        	The proper way to reset the state of an event object used with the WSAEventSelect function
        	is to pass the handle of the event object to the WSAEnumNetworkEvents function in the hEventObject parameter.
        	This will reset the event object and adjust the status of active FD events on the socket in an atomic fashion.
        	*/
        /* WSAResetEvent(active_event); <== DO NOT USE (see above) */

done:
        /* unlock context */
        tsk_safeobj_unlock(context);
    } /* while(transport->running) */


bail:

    TSK_DEBUG_INFO("Stopped [%s] server with IP {%s} on port {%d} with type {%d}...", transport->description, transport->master->ip, transport->master->port, transport->master->type);
    return tsk_null;
}
int tnet_transport_prepare(tnet_transport_t *transport)
{
	int ret = -1;
	transport_context_t *context;
	
	if(!transport || !transport->context){
		TSK_DEBUG_ERROR("Invalid parameter.");
		return -1;
	}
	else{
		context = transport->context;
	}
	
	if(transport->prepared){
		TSK_DEBUG_ERROR("Transport already prepared.");
		return -2;
	}
	
	/* Start listening */
	if(TNET_SOCKET_TYPE_IS_STREAM(transport->master->type)){
		if((ret = tnet_sockfd_listen(transport->master->fd, WSA_MAXIMUM_WAIT_EVENTS))){
			TNET_PRINT_LAST_ERROR("listen have failed.");
			goto bail;
		}
	}
	
	/* Add the master socket to the context. */
	if((ret = addSocket(transport->master->fd, transport->master->type, transport, tsk_true, tsk_false))){
		TSK_DEBUG_ERROR("Failed to add master socket");
		goto bail;
	}
	
	/* set events on master socket */
	if((ret = WSAEventSelect(transport->master->fd, context->events[context->count - 1], TNET_SOCKET_TYPE_IS_DGRAM(transport->master->type) ? FD_READ : FD_ALL_EVENTS/*FD_ACCEPT | FD_READ | FD_CONNECT | FD_CLOSE*/) == SOCKET_ERROR)){
		TNET_PRINT_LAST_ERROR("WSAEventSelect have failed.");
		goto bail;
	}
	
	transport->prepared = tsk_true;
	
bail:
	return ret;
}