//=================================================================================================
//	SOCKET object definition
//
static tsk_object_t* tnet_socket_ctor(tsk_object_t * self, va_list * app)
{
	tnet_socket_t *sock = self;
	if(sock){
		int status;
		tsk_bool_t nonblocking;
		tsk_bool_t bindsocket;
		tsk_istr_t port;
		struct addrinfo *result = 0;
		struct addrinfo *ptr = 0;
		struct addrinfo hints;
		tnet_host_t local_hostname;

		const char *host = va_arg(*app, const char*);
#if defined(__GNUC__)
		sock->port = (tnet_port_t)va_arg(*app, unsigned);
#else
		sock->port = va_arg(*app, tnet_port_t);
#endif
		tsk_itoa(sock->port, &port);
		sock->type = va_arg(*app, tnet_socket_type_t);
		nonblocking = va_arg(*app, tsk_bool_t);
		bindsocket = va_arg(*app, tsk_bool_t);

		memset(local_hostname, 0, sizeof(local_hostname));

		/* Get the local host name */
		if(host != TNET_SOCKET_HOST_ANY && !tsk_strempty(host)){
			memcpy(local_hostname, host, tsk_strlen(host)>sizeof(local_hostname)-1 ? sizeof(local_hostname)-1 : tsk_strlen(host));
		}
		else{
			if(TNET_SOCKET_TYPE_IS_IPV6(sock->type)){
				memcpy(local_hostname, "::", 2);
			}
			else{
				memcpy(local_hostname, "0.0.0.0", 7);
			}
			//if((status = tnet_gethostname(&local_hostname)))
			//{
			//	TNET_PRINT_LAST_ERROR("gethostname have failed.");
			//	goto bail;
			//}
		}

		/* hints address info structure */
		memset(&hints, 0, sizeof(hints));
		hints.ai_family = TNET_SOCKET_TYPE_IS_IPV46(sock->type) ? AF_UNSPEC : (TNET_SOCKET_TYPE_IS_IPV6(sock->type) ? AF_INET6 : AF_INET);
		hints.ai_socktype = TNET_SOCKET_TYPE_IS_STREAM(sock->type) ? SOCK_STREAM : SOCK_DGRAM;
		hints.ai_protocol = TNET_SOCKET_TYPE_IS_STREAM(sock->type) ? IPPROTO_TCP : IPPROTO_UDP;
		hints.ai_flags = AI_PASSIVE
#if !TNET_UNDER_WINDOWS || _WIN32_WINNT>=0x600
			| AI_ADDRCONFIG
#endif
			;

		/* Performs getaddrinfo */
		if((status = tnet_getaddrinfo(local_hostname, port, &hints, &result))){
			TNET_PRINT_LAST_ERROR("tnet_getaddrinfo(family=%d, hostname=%s and port=%s) failed: [%s]", 
				hints.ai_family, local_hostname, port, tnet_gai_strerror(status));
			goto bail;
		}
		
		/* Find our address. */
		for(ptr = result; ptr; ptr = ptr->ai_next){
			sock->fd = tnet_soccket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
			if(ptr->ai_family != AF_INET6 && ptr->ai_family != AF_INET){
				continue;
			}
			
			if(bindsocket){
				/* Bind the socket */
				if((status = bind(sock->fd, ptr->ai_addr, ptr->ai_addrlen))){
					TNET_PRINT_LAST_ERROR("bind have failed.");
					tnet_socket_close(sock);
					continue;
				}

				/* Get local IP string. */
				if(status = tnet_get_ip_n_port(sock->fd , &sock->ip, &sock->port)) /* % */
				//if((status = tnet_getnameinfo(ptr->ai_addr, ptr->ai_addrlen, sock->ip, sizeof(sock->ip), 0, 0, NI_NUMERICHOST)))
				{
					TNET_PRINT_LAST_ERROR("Failed to get local IP and port.");
					tnet_socket_close(sock);
					continue;
				}
//				else{
//#if TNET_UNDER_WINDOWS
//					int index;
//					if((index = tsk_strindexOf(sock->ip, tsk_strlen(sock->ip), "%")) > 0){
//						*(sock->ip + index) = '\0';
//					}
//#endif
//				}
			}

			/* sets the real socket type (if ipv46) */
			if(ptr->ai_family == AF_INET6) {
				TNET_SOCKET_TYPE_SET_IPV6Only(sock->type);
			}
			else{
				TNET_SOCKET_TYPE_SET_IPV4Only(sock->type);
			}
			break;
		}
		
		/* Check socket validity. */
		if(!TNET_SOCKET_IS_VALID(sock)) {
			TNET_PRINT_LAST_ERROR("Invalid socket.");
			goto bail;
		}		

		/* To avoid "Address already in use" error */
		{
#if defined(SOLARIS)
			char yes = '1';
#else
			int yes = 1;
#endif
			if(setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(int))){
				TNET_PRINT_LAST_ERROR("setsockopt(SO_REUSEADDR) have failed.");
			}
		}

		/* Sets the socket to nonblocking mode */
		if(nonblocking){
			if((status = tnet_sockfd_set_nonblocking(sock->fd))){
				goto bail;
			}
		}

bail:
		/* Free addrinfo */
		tnet_freeaddrinfo(result);

		/* Close socket if failed. */
		if(status && TNET_SOCKET_IS_VALID(sock)){
			tnet_socket_close(sock);
		}

	}
	return self;
}
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;
}
Exemple #3
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;
}
Exemple #4
0
/**@ingroup tnet_socket_group
* Creates a new socket.
* To check that the returned socket is valid use @ref TNET_SOCKET_IS_VALID function.
* @param host FQDN (e.g. www.doubango.org) or IPv4/IPv6 IP string.
* @param port The local/remote port used to receive/send data. Set the port value to @ref TNET_SOCKET_PORT_ANY to bind to a random port.
* @param type The type of the socket. See @ref tnet_socket_type_t.
* @param nonblocking Indicates whether to create non-blocking socket.
* @param bindsocket Indicates whether to bind the newly created socket or not.
* @retval @ref tnet_socket_t object.
* @sa @ref tnet_socket_create.
*/
tnet_socket_t* tnet_socket_create_2(const char* host, tnet_port_t port_, tnet_socket_type_t type, tsk_bool_t nonblocking, tsk_bool_t bindsocket)
{
	tnet_socket_t *sock;
	if ((sock = tsk_object_new(tnet_socket_def_t))) {
		int status;
		tsk_istr_t port;
		struct addrinfo *result = tsk_null;
		struct addrinfo *ptr = tsk_null;
		struct addrinfo hints;
		tnet_host_t local_hostname;

		sock->port = port_;
		tsk_itoa(sock->port, &port);
		sock->type = type;

		memset(local_hostname, 0, sizeof(local_hostname));

		/* Get the local host name */
		if (host != TNET_SOCKET_HOST_ANY && !tsk_strempty(host)){
			memcpy(local_hostname, host, tsk_strlen(host) > sizeof(local_hostname) - 1 ? sizeof(local_hostname) - 1 : tsk_strlen(host));
		}
		else{
			if (TNET_SOCKET_TYPE_IS_IPV6(sock->type)){
				memcpy(local_hostname, "::", 2);
			}
			else {
				memcpy(local_hostname, "0.0.0.0", 7);
			}
		}

		/* hints address info structure */
		memset(&hints, 0, sizeof(hints));
		hints.ai_family = TNET_SOCKET_TYPE_IS_IPV46(sock->type) ? AF_UNSPEC : (TNET_SOCKET_TYPE_IS_IPV6(sock->type) ? AF_INET6 : AF_INET);
		hints.ai_socktype = TNET_SOCKET_TYPE_IS_STREAM(sock->type) ? SOCK_STREAM : SOCK_DGRAM;
		hints.ai_protocol = TNET_SOCKET_TYPE_IS_STREAM(sock->type) ? IPPROTO_TCP : IPPROTO_UDP;
		hints.ai_flags = AI_PASSIVE
#if !TNET_UNDER_WINDOWS || _WIN32_WINNT>=0x600
			| AI_ADDRCONFIG
#endif
			;

		/* Performs getaddrinfo */
		if ((status = tnet_getaddrinfo(local_hostname, port, &hints, &result))) {
			TNET_PRINT_LAST_ERROR("tnet_getaddrinfo(family=%d, hostname=%s and port=%s) failed: [%s]",
				hints.ai_family, local_hostname, port, tnet_gai_strerror(status));
			goto bail;
	}

		/* Find our address. */
		for (ptr = result; ptr; ptr = ptr->ai_next){
			sock->fd = (tnet_fd_t)tnet_soccket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
			if (ptr->ai_family != AF_INET6 && ptr->ai_family != AF_INET){
				continue;
			}
			/* To avoid "Address already in use" error
			* Check issue 368 (https://code.google.com/p/doubango/issues/detail?id=368) to understand why it's not used for UDP/DTLS.
			*/
			//
			if (TNET_SOCKET_TYPE_IS_STREAM(sock->type)) {
				if ((status = tnet_sockfd_reuseaddr(sock->fd, 1))) {
					// do not break...continue
				}
			}

			if (bindsocket){
				/* Bind the socket */
				if ((status = bind(sock->fd, ptr->ai_addr, (int)ptr->ai_addrlen))){
					TNET_PRINT_LAST_ERROR("bind to [%s:%s]have failed", local_hostname, port);
					tnet_socket_close(sock);
					continue;
				}

				/* Get local IP string. */
				if ((status = tnet_get_ip_n_port(sock->fd, tsk_true/*local*/, &sock->ip, &sock->port))) /* % */
					//if((status = tnet_getnameinfo(ptr->ai_addr, ptr->ai_addrlen, sock->ip, sizeof(sock->ip), 0, 0, NI_NUMERICHOST)))
				{
					TNET_PRINT_LAST_ERROR("Failed to get local IP and port.");
					tnet_socket_close(sock);
					continue;
				}
			}

			/* sets the real socket type (if ipv46) */
			if (ptr->ai_family == AF_INET6) {
				TNET_SOCKET_TYPE_SET_IPV6Only(sock->type);
			}
			else {
				TNET_SOCKET_TYPE_SET_IPV4Only(sock->type);
			}
			break;
		}

		/* Check socket validity. */
		if (!TNET_SOCKET_IS_VALID(sock)) {
			TNET_PRINT_LAST_ERROR("Invalid socket.");
			goto bail;
		}

#if TNET_UNDER_IPHONE || TNET_UNDER_IPHONE_SIMULATOR
		/* disable SIGPIPE signal */
		{
			int yes = 1;
			if (setsockopt(sock->fd, SOL_SOCKET, SO_NOSIGPIPE, (char*)&yes, sizeof(int))){
				TNET_PRINT_LAST_ERROR("setsockopt(SO_NOSIGPIPE) have failed.");
			}
		}
#endif /* TNET_UNDER_IPHONE */

		/* Sets the socket to nonblocking mode */
		if(nonblocking){
			if((status = tnet_sockfd_set_nonblocking(sock->fd))){
				goto bail;
			}
		}

	bail:
		/* Free addrinfo */
		tnet_freeaddrinfo(result);

		/* Close socket if failed. */
		if (status){
			if (TNET_SOCKET_IS_VALID(sock)){
				tnet_socket_close(sock);
			}
			return tsk_null;
		}
}

	return sock;
}