/** \brief callback notified by \ref upnp_call_t on completion
 */
bool	upnp_call_extipaddr_t::neoip_upnp_call_cb(void *cb_userptr, upnp_call_t &cb_call
				, const upnp_err_t &cb_upnp_err, const strvar_db_t &strvar_db)	throw()
{
	// log to debug
	KLOG_DBG("enter upnp_err=" << cb_upnp_err << " strvar_db=" << strvar_db);
	
	// if the upnp_call_t failed, forward the upnp_err_t
	if( cb_upnp_err.failed() )	return notify_callback(cb_upnp_err, ip_addr_t());
	
	// if the upnp_call_t replied strvar_db doesnt contain "NewExternalIPAddr", notify an error
	if( !strvar_db.contain_key("NewExternalIPAddress") ){
		upnp_err_t upnp_err(upnp_err_t::ERROR, "unable to find NewExternalIPAddress in upnp_call_t response");
		return notify_callback(upnp_err, ip_addr_t());
	}

	// try to convert the content into a ip_addr_t
	ip_addr_t extipaddr	= strvar_db.get_first_value("NewExternalIPAddress");
	// if the content can't be parse as an ip_addr_t, notify an error
	if( !extipaddr.is_fully_qualified() ){
		upnp_err_t upnp_err(upnp_err_t::ERROR, "unable to parse NewExternalIPAddress as ip_addr_t");
		return notify_callback(upnp_err, ip_addr_t());
	}
	
	// NOTE: if this point is reached, the upnp_call_extipaddr_t has been successfull, notify the caller
	return notify_callback(upnp_err_t::OK, extipaddr);
}
Ejemplo n.º 2
0
/** \brief Parse the "peers" fields returned by the http reply from the tracker
 * 
 * - when it is a 'compact' reply
 */
bt_tracker_peer_arr_t	bt_tracker_reply_t::dvar_to_peer_arr_docompact(const dvar_t &peers_dvar)	throw()
{
	bt_tracker_peer_arr_t	peer_arr;
	// log to debug
	KLOG_DBG("peers_dvar=" << peers_dvar);
	// sanity check - the peers_dvar MUST be a dvar_type_t::STRING
	DBG_ASSERT( peers_dvar.type() == dvar_type_t::STRING );
	// convert the peers_dvar into a bytearray_t to ease the parsing	
	bytearray_t	bytearray	= bytearray_t(datum_t(peers_dvar.str().get()));
	// parse the bytearray_t - it is a suite of ipv4 address / port
	while( !bytearray.empty() ){
		uint32_t	ipaddr;
		uint16_t	port;
		try {
			bytearray >> ipaddr;
			bytearray >> port;
		}catch(serial_except_t &e){
			DBGNET_ASSERT( 0 );
			return bt_tracker_peer_arr_t();
		}
		// add the bt_tracker_peer_t in the bt_tracker_peer_arr_t
		ipport_addr_t	ipport_addr(ip_addr_t(ipaddr), port);
		// if the ipport_addr_t is not is_fully_qualified, discard it
		// - NOTE: this is a workaround a bug seen where http tracker which gives port = 0
		//   - a similar bug has been seen in utpex from utorrent1.6
		if( !ipport_addr.is_fully_qualified() )	continue;
		// add the new bt_tracker_peer_t in the peer_arr
		peer_arr	+= bt_tracker_peer_t( ipport_addr, bt_id_t() );
	}
	// return the result 
	return peer_arr;
}
Ejemplo n.º 3
0
/** \brief Overload the substraction
 */
ip_addr_t	ip_addr_t::operator- (const uint32_t val)	const throw()
{
	// sanity check - currently only handle ipv4	
	DBG_ASSERT( is_v4() );
	// return the result
	return ip_addr_t( get_v4_addr() - val );
}
Ejemplo n.º 4
0
/** \brief test the comparison operator
 */
nunit_res_t	ip_addr_testclass_t::comparison_op(const nunit_testclass_ftor_t &testclass_ftor) throw()
{
	NUNIT_ASSERT( ip_addr_t() < ip_addr_t("0.0.0.0") );
	NUNIT_ASSERT( ip_addr_t("10.0.0.1") < ip_addr_t("10.0.0.2") );
	NUNIT_ASSERT( ip_addr_t("12.0.0.1") != ip_addr_t("10.0.0.0") );
	NUNIT_ASSERT( ip_addr_t("1.2.3.4") <= ip_addr_t("2.3.4.5"));
	// return no error
	return NUNIT_RES_OK;
}
Ejemplo n.º 5
0
/** \brief general test 
 */
nunit_res_t	ip_addr_testclass_t::general(const nunit_testclass_ftor_t &testclass_ftor) throw()
{
	NUNIT_ASSERT( ip_addr_t("10.1.2.3").is_private()	);
	NUNIT_ASSERT( ip_addr_t("192.168.1.2").is_private()	);
	NUNIT_ASSERT( ip_addr_t("127.1.2.3").is_localhost()	);
	NUNIT_ASSERT( ip_addr_t("1.2.3.4").is_public()		);
	NUNIT_ASSERT( ip_addr_t("0.0.0.0").is_any()		);
	NUNIT_ASSERT( ip_addr_t("255.255.255.255").is_broadcast());
	NUNIT_ASSERT( ip_addr_t("1.2.3.4").is_v4()		);
	NUNIT_ASSERT( ip_addr_t("1.2.3.4").get_version() == 4	);
	NUNIT_ASSERT( ip_addr_t("1.2.3.4").get_v4_addr() == 0x01020304);

	// return no error
	return NUNIT_RES_OK;
}
/** \brief set from a string
 */
socket_err_t	socket_peerid_udp_t::ctor_from_str(const std::string &str)	throw()
{
	// parse the input
	ipaddr	= ip_addr_t(str.c_str());
	// if an error occur while parsing this, report it now
	if( ipaddr.is_null() )
		return socket_err_t(socket_err_t::BAD_PARAM, "bad socket_peerid_udp_t format in "+ str);
	// return no error
	return socket_err_t::OK;
}
Ejemplo n.º 7
0
/** \brief set the ip_addr_t from a string
 * 
 * @return false if no error occured, true otherwise
 */
bool ip_addr_t::from_string(const std::string &ip_addr_str) throw()
{
	struct	in_addr	inaddr;
	inet_err_t	inet_err;
	// do the inet_aton
	inet_err	= inet_oswarp_t::inet_aton(ip_addr_str.c_str(), &inaddr);
	if( inet_err.failed() )	return true;
	// set the object
	*this = ip_addr_t(ntohl(inaddr.s_addr));
	return false;
}
Ejemplo n.º 8
0
/** \brief callback notified when asyncexe_t has an event to report
 */
bool	host2ip_fork_t::neoip_asyncexe_cb(void *cb_userptr, asyncexe_t &cb_asyncexe
					, const libsess_err_t &libsess_err
					, const bytearray_t &stdout_barray
					, const int &exit_status)	throw()
{
	// log to debug
	KLOG_ERR("enter libsess_err="<< libsess_err << " exit_status=" << exit_status
						<< " stdout_barray=" << stdout_barray);

	// if exit_status is non null, notify the called
	if( exit_status != 0 ){
		std::string reason	= "neoip-dnsresolve.sh returned error " + OSTREAMSTR(exit_status);
		return notify_callback_err(inet_err_t(inet_err_t::ERROR, reason));
	}

	/*************** parse the output_barray	***********************/
	std::string			recved_str	= stdout_barray.to_stdstring();
	std::vector<std::string> 	arr_str		= string_t::split(recved_str, "/");
	std::vector<ip_addr_t>		arr_ipaddr;
	// parse the arr_str into arr_ipaddr
	for(size_t i = 0; i < arr_str.size(); i++){
		// if this string is empty, leave the loop
		if( arr_str[i].empty() )	break;
		// sanity check - the non empty string MUST be convertible in a ip_addr_t
		DBGNET_ASSERT( !ip_addr_t(arr_str[i]).is_null() );
		// else add the ip_addr_t to the arr_ipaddr
		arr_ipaddr.push_back(ip_addr_t(arr_str[i]));
	}
	// notify the caller with a faillure if the arr_ipaddr is empty
	if( arr_ipaddr.size() == 0 ){
		std::string reason	= "resolution failed";
		return notify_callback_err(inet_err_t(inet_err_t::ERROR, reason));
	}

	// else notify the caller with a success
	return notify_callback(inet_err_t::OK, arr_ipaddr);
}
Ejemplo n.º 9
0
/** \brief Test function
 */
nunit_res_t	ipcountry_testclass_t::general(const nunit_testclass_ftor_t &testclass_ftor) throw()
{
	inet_err_t	inet_err;
	// log to debug
	KLOG_DBG("enter");

	// start the ipcountry_t
	ipcountry	= nipmem_new ipcountry_t();
	inet_err	= ipcountry->start(ip_addr_t("62.70.27.118"), this, NULL);
	NUNIT_ASSERT( inet_err.succeed() );

	// copy the functor to report nunit_res_t asynchronously
	nunit_ftor	= testclass_ftor;
	return NUNIT_RES_DELAYED;
}
Ejemplo n.º 10
0
/** \brief Test the serialization consistency
 */
nunit_res_t	ip_addr_testclass_t::serial_consistency(const nunit_testclass_ftor_t &testclass_ftor) throw()
{
	ip_addr_t	ip_addr_toserial	= "127.0.0.1";
	ip_addr_t	ip_addr_unserial;
	serial_t	serial;
	// test with ip_addr_t equal to a value
	serial << ip_addr_toserial;
	serial >> ip_addr_unserial;
	NUNIT_ASSERT( ip_addr_toserial == ip_addr_unserial );

	// test with ip_addr_t equal to NULL
	ip_addr_toserial = ip_addr_t();
	serial << ip_addr_toserial;
	serial >> ip_addr_unserial;
	NUNIT_ASSERT( ip_addr_toserial == ip_addr_unserial );

	// return no error
	return NUNIT_RES_OK;
}
Ejemplo n.º 11
0
/** \brief Pick a inner ip address from the available one (may return null if none is available)
 */
ip_addr_t	router_resp_cnx_t::pick_inner_addr(const ip_addr_inval_t &avail_iaddr
					, const ip_addr_t &prefered_iaddr)	throw()
{
	// if no address is available, return a null ip_addr_t
	if( avail_iaddr.size() == 0 )	return ip_addr_t();
	
	// if the prefered address is not null and is contained in avail_iaddr, pick this one
	if( !prefered_iaddr.is_null() && avail_iaddr.contain(prefered_iaddr) )
		return prefered_iaddr;

	// TODO find a better algorithm 
	// - this one is too deterministic and will cause negociation retry when 2 tunnel are established
	//   at the same time. 
	// - to pick one at random on the first item? seems better
	//   - do i have the ip_addr_t arithmetic operator to do it ?
	
	// return the lowest of the interval
	return avail_iaddr[0].min_value();
}
Ejemplo n.º 12
0
/** \brief Start the operation
 */
inet_err_t	ipcountry_t::start(const ip_addr_t &m_ipaddr, ipcountry_cb_t *callback, void *userptr)	throw()
{
	// copy the parameter
	this->m_ipaddr	= m_ipaddr;
	this->callback	= callback;
	this->userptr	= userptr;

	// build the hostname to resolve - ip_addr_t in reverse order appended with .zz.countries.nerd.dk
	std::string	hostname;
	hostname	= ip_addr_t(NEOIP_HTONL(ipaddr().get_v4_addr())).to_string();
	hostname	+= ".zz.countries.nerd.dk";

	// log to debug
	KLOG_DBG("try to resolve hostname=" << hostname);

	// launch the host2ip_t on the just built hostname
	inet_err_t	inet_err;
	host2ip		= nipmem_new host2ip_t();
	inet_err	= host2ip->start(hostname, this, NULL);
	if( inet_err.failed() )		return inet_err;

	// return no error
	return inet_err_t::OK;
}
Ejemplo n.º 13
0
/** \brief callback notified by \ref upnp_disc_t on completio\n
 */
bool	upnp_watch_t::neoip_upnp_disc_cb(void *cb_userptr, upnp_disc_t &cb_disc
				, const upnp_err_t &cb_upnp_err
				, const upnp_disc_res_t &notified_disc_res)	throw()
{
	upnp_err_t	upnp_err	= cb_upnp_err;
	// log to debug
	KLOG_DBG("enter upnp_err=" << upnp_err << " disc_res=" << notified_disc_res);

	// sanity check - if upnp_err.succeed() then disc_res MUST be non-null
	if( upnp_err.succeed() )	DBG_ASSERT( !notified_disc_res.is_null() );
	// sanity check - if upnp_err.failed() then disc_res MUST be null
	if( upnp_err.failed() )		DBG_ASSERT(  notified_disc_res.is_null() );
	// sanity check - the disc_timeout MUST NOT be running during upnp_disc_t execution
	DBG_ASSERT( !disc_timeout.is_running() );

	// determine if the upnp_disc_res_t changed or not  
	bool	res_changed;
	if( notified_disc_res == current_disc_res() )	res_changed	= false;
	else						res_changed	= true;


#if 1	// TODO to remove - only to debug assert failing in some case of UPNP_CHANGED
	upnp_disc_res_t	old_disc_res	= m_current_disc_res;
#endif	
	// copy the new upnp_disc_res_t
	m_current_disc_res	= notified_disc_res;

	// delete the upnp_disc_t
	nipmem_zdelete upnp_disc;

	// if the upnp_disc_res_t changed, notify the caller
	if( res_changed ){
		bool	tokeep	= notify_callback(upnp_watch_event_t::build_upnpdisc_changed());
		if( !tokeep )	return false;
	}


	// relaunch the disc_timeout for the next upnp_disc_t
	disc_timeout.start(disc_delaygen.pre_inc(), this, NULL);
	
#if 0	// TODO not sure about this one
	// if upnp_unavail() now, reset the m_extipaddr
	// - TODO should i notify this event ?
	if( upnp_unavail() )	m_extipaddr	= ip_addr_t();
#endif

	// if the res_changed and now upnp_isavail(), start the extipaddr_timeout and portcleaner_timeout
	if( res_changed && upnp_isavail() ){
		// sanity check: the extipaddr_timeout MUST NOT be running
		// - TODO this assert fails from time to time... fix this
		//   - i dont even see how it can be right to have this as this 'if' 
		//     may be entered at anytime
		//   - instead, would be way better to delete call_extipaddr if needed
#if 1	// TODO to remove - only to debug assert failing in some case of UPNP_CHANGED
	// - this is due to getporttest sometime being 0 on a router in which is it 1
	//   - so for unknown reason sometime this is diagnosed as
	//   - is it due to a timeout ? and using the default value as 0 ?
	//   - apparently getportendian_test report an error on timeout... so this
	//     cant be it...
	//   - how it is possible to get a 0 while the upnp router is a 1 ?
	//   - what about a race ?
	//   - it uses a constant "upnp revendian testing" tag to identify the addport
	//     SO if 2 getportend_test are done simultaneously, this MAY result in a conflict
	//     - likely rare, but trivial to fix. 
	//     - TODO just put a nonce in the tag
	// - res_changed ISNOT upnp is avail or not, the result may change even
	//   between isavail and isavail
	//   - as shown in changing from getporttest false to getposttest true
	//   - SO DO NOT assume that it is 
		if( !(!extipaddr_timeout.is_running() && !call_extipaddr) ){
			KLOG_ERR("new disc_rec=" << m_current_disc_res);
			KLOG_ERR("old disc_rec=" << old_disc_res);
		}
		DBG_ASSERT( !extipaddr_timeout.is_running() );
		DBG_ASSERT( !call_extipaddr );
#else
		DBG_ASSERT( !extipaddr_timeout.is_running() && !call_extipaddr );
#endif
		// init the extipaddr_timeout
		extipaddr_delaygen	= delaygen_t(profile.extipaddr_delaygen_arg());
		extipaddr_timeout.start(extipaddr_delaygen.current(), this, NULL);
		// init the portcleaner_timeout - IIF profile has portcleaner_enabled
		if( get_profile().portcleaner_enabled() ){
			// - sanity check: upnp_portcleaner_t MUST NOT be running
			DBG_ASSERT( !upnp_portcleaner );
			upnp_portcleaner= nipmem_new upnp_portcleaner_t();
			upnp_err	= upnp_portcleaner->start(this);
			DBG_ASSERT( upnp_err.succeed() );
		}
	}

	// if the res_changed and now upnp_unavail(), stop the extipaddr_timeout and portcleaner_timeout
	if( res_changed && upnp_unavail() ){
		// stop the extipaddr_timeout
		// - sanity check: the extipaddr_timeout MUST be running
		DBG_ASSERT( extipaddr_timeout.is_running() || call_extipaddr );
		extipaddr_timeout.stop();
		nipmem_zdelete	call_extipaddr;
		// stop the portcleaner_timeout - IIF profile has portcleaner_enabled
		if( get_profile().portcleaner_enabled() ){
			// - sanity check: upnp_portcleaner_t  MUST be running
			DBG_ASSERT( upnp_portcleaner );
			// delete upnp_portcleaner
			nipmem_zdelete	upnp_portcleaner;
		}
	}

	// return 'dontkeep' as the upnp_disc_t has been deleted
	return false;
}
Ejemplo n.º 14
0
/** \brief Build a upnp_portdesc_t from a strvar_db_t 
 *
 * - this is the strvar_db_t replied from a upnp_call_t
 */
upnp_portdesc_t	upnp_portdesc_t::from_strvar_db(const strvar_db_t &strvar_db, bool revendian)	throw()
{	
	upnp_portdesc_t	portdesc;
	// log to debug
	KLOG_DBG("enter revendian=" << revendian << " strvar_db=" << strvar_db);

	/*************** parse the NewExternalPort port	***********************/
	if( strvar_db.contain_key("NewExternalPort") ){
		// try to convert the content into a port
		uint16_t port_pview	= atoi(strvar_db.get_first_value("NewExternalPort").c_str());
		if( revendian )		port_pview = endianswap_t::swap16(port_pview);

		/*************** parse the NewExternalHost ip_addr_t	***************/
		if( !strvar_db.contain_key("NewRemoteHost") )	return upnp_portdesc_t();
		// try to convert the content into a port
		std::string	ipaddr_pview_str	= strvar_db.get_first_value("NewRemoteHost");
		ip_addr_t	ipaddr_pview		= ipaddr_pview_str.size() ? ipaddr_pview_str : "0.0.0.0";
		if( revendian && ipaddr_pview.is_v4() )
			ipaddr_pview	= ip_addr_t(endianswap_t::swap32(ipaddr_pview.get_v4_addr()));
	
		// copy the result into the internal variable
		portdesc.ipport_pview	( ipport_addr_t(ipaddr_pview, port_pview) );
	}

	/*************** parse the NewProtocol upnp_sockfam_t	***************/
	if( strvar_db.contain_key("NewProtocol") ){
		std::string str	= strvar_db.get_first_value("NewProtocol");
		portdesc.sockfam	( upnp_sockfam_t::from_string_nocase(str) );
	}
	
	/*************** parse the NewInternalPort port	***********************/
	if( !strvar_db.contain_key("NewInternalPort") )		return upnp_portdesc_t();
	// try to convert the content into a port
	uint16_t port_lview	= atoi(strvar_db.get_first_value("NewInternalPort").c_str());
	if( revendian )		port_lview = endianswap_t::swap16(port_lview);

	/*************** parse the NewInternalClient ip_addr_t	***************/
	if( !strvar_db.contain_key("NewInternalClient") )	return upnp_portdesc_t();
	// try to convert the content into a ip_addr_t
	ip_addr_t ipaddr_lview	= strvar_db.get_first_value("NewInternalClient");
	if( revendian && ipaddr_lview.is_v4() )
		ipaddr_lview = ip_addr_t(endianswap_t::swap32(ipaddr_lview.get_v4_addr()));
	
	// if the content can't be parse as an ip_addr_t, notify an error
	if( !ipaddr_lview.is_fully_qualified() )		return upnp_portdesc_t();

	// copy the result into the internal variable
	portdesc.ipport_lview	( ipport_addr_t(ipaddr_lview, port_lview) );
	DBG_ASSERT( portdesc.ipport_lview().is_fully_qualified() );

	/*************** parse the NewEnabled	*******************************/
	if( !strvar_db.contain_key("NewEnabled") )		return upnp_portdesc_t();
	// try to convert the content into a std::string
	portdesc.map_enabled	( atoi(strvar_db.get_first_value("NewEnabled").c_str()) ? true : false ); 

	/*************** parse the NewPortMappingDescription	***************/
	if( !strvar_db.contain_key("NewPortMappingDescription"))return upnp_portdesc_t();	
	portdesc.desc_str	( strvar_db.get_first_value("NewPortMappingDescription") );

	/*************** parse the portcleaner_tag if needed	***************/
	delay_t		portcleaner_lease;
	std::string	portcleaner_nonce;
	std::string	real_desc	= upnp_portcleaner_t::parse_tag_desc(portdesc.desc_str()
						, portcleaner_lease, portcleaner_nonce);
	portdesc.desc_str		( real_desc		);
	portdesc.portcleaner_lease	( portcleaner_lease	);
	portdesc.portcleaner_nonce	( portcleaner_nonce	);

	/*************** parse the NewLeaseDuration	***********************/
	if( !strvar_db.contain_key("NewLeaseDuration") )	return upnp_portdesc_t();
	std::string	lease_delay_str	= strvar_db.get_first_value("NewLeaseDuration");
	delay_t		lease_delay	= delay_t::from_sec(atoi(lease_delay_str.c_str()));
	// handle the special case of 0sec means INFINITE
	if( lease_delay == delay_t::from_sec(0) )	lease_delay = delay_t::INFINITE;
	portdesc.lease_delay	( lease_delay );
	
	// sanity check - here portdesc MUST NOT be null
	DBG_ASSERT( !portdesc.is_null() );
	// return the just built upnp_portdesc_t
	return portdesc;
}
Ejemplo n.º 15
0
signed int
main(signed int ac, char** av)
{
	SSL_CTX* 	ctx(nullptr);
	SSL_METHOD*	meth(nullptr);
	SSL*		ssl(nullptr);
	signed long	sflags(0);
	signed int	sockfd(-1);
	signed int	retval(-1);
	std::string	host("");
	std::string	port("");
	std::string key("");
	std::string cert("");
	std::string	store("");
	std::string root("");

	::SSL_library_init();
	::OpenSSL_add_all_algorithms();
	::SSL_load_error_strings();

	sflags = ( 	SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | 
				SSL_OP_NO_TLSv1_1 | SSL_OP_CIPHER_SERVER_PREFERENCE | 
				SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION );

	if (7 != ac) {
		std::cerr << "Usage: " << av[0] << " <host> <port> <certificate> <key> <root certificate> <certificate store>" << std::endl;
		return EXIT_FAILURE;
	}

	host 	= av[1];
	port 	= av[2];
	cert	= av[3];
	key 	= av[4];
	root	= av[5];
	store	= av[6];

	meth	= const_cast< SSL_METHOD* >(::TLSv1_2_client_method());
	ctx		= ::SSL_CTX_new(meth);

	if (nullptr == meth) {
		::ERR_print_errors_fp(stderr);
		return EXIT_FAILURE;
	}

	::SSL_CTX_set_options(ctx, sflags);

	if (0 >= ::SSL_CTX_use_certificate_file(ctx, cert.c_str(), SSL_FILETYPE_PEM)) {
		::ERR_print_errors_fp(stderr);
		return EXIT_FAILURE;
	}

	if (0 >= ::SSL_CTX_use_PrivateKey_file(ctx, key.c_str(), SSL_FILETYPE_PEM)) {
		::ERR_print_errors_fp(stderr);
		return EXIT_FAILURE;
	}

	if (! ::SSL_CTX_check_private_key(ctx)) {
		std::cerr << "Private key does not match certificate" << std::endl;
		return EXIT_FAILURE;
	}

	if (! ::SSL_CTX_load_verify_locations(ctx, root.c_str(), store.c_str())) {
		std::cerr << "Load certificate store location failure" << std::endl;
		return EXIT_FAILURE;
	}

	::SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); //&verify_cb);
	::SSL_CTX_set_verify_depth(ctx, 5);

	std::cout << "Connecting to: " << host << ":" << port << std::endl;

	sockfd = open_connection(host, port);

	if (0 > sockfd) 
		return EXIT_FAILURE;

	ssl = SSL_new(ctx);
	::SSL_set_fd(ssl, sockfd);

	do {
		retval = ::ERR_get_error();
		
		if (0 != retval) 
			std::cerr << "SSL ERR: " << ::ERR_reason_error_string(retval) << std::endl;

	} while (0 != retval);

	retval = ::SSL_connect(ssl);

	if (1 != retval) {
		std::cerr << "Error in SSL_connect()" << std::endl;
		::ERR_print_errors_fp(stderr);


	} else {
		X509* 			ccert(::SSL_get_peer_certificate(ssl));
		char* 			line(nullptr);
		char 			buf[4096] = {0};
		signed int		len(-1);
		struct in_addr	ia;
		struct in6_addr	i6a;
		std::vector< ip_addr_t > avec;
		std::vector< port_t >	pvec;

		//message_t		msg(OP_SCAN_STATUS, 0x4141414141414141);


		if (0 >= ::inet_pton(AF_INET, "127.0.0.0", &ia)) {
			std::cerr << "inet_pton() error: " << ::strerror(errno) << std::endl;
			return EXIT_FAILURE;
		}

		avec.push_back(ip_addr_t(ia, 24));
		
		if (0 >= ::inet_pton(AF_INET, "192.0.0.0", &ia)) {
			std::cerr << "inet_pton() error: " << ::strerror(errno) << std::endl;
			return EXIT_FAILURE;
		}

		avec.push_back(ip_addr_t(ia, 8));

		if (0 >= ::inet_pton(AF_INET6, "fe80:20c:29ff:feee:4b72::1", &i6a)) {
			std::cerr << "inet_pton() error: " << ::strerror(errno) << std::endl;
			return EXIT_FAILURE;
		}

		avec.push_back(ip_addr_t(i6a, 120));

		if (0 >= ::inet_pton(AF_INET, "10.0.0.1", &ia)) {
			std::cerr << "inet_pton() error: " << ::strerror(errno) << std::endl;
			return EXIT_FAILURE;
		}

		avec.push_back(ip_addr_t(ia));

		if (0 >= ::inet_pton(AF_INET6, "::1", &i6a)) {
			std::cerr << "inet_pton() error: " << ::strerror(errno) << std::endl;
			return EXIT_FAILURE;
		}

		avec.push_back(ip_addr_t(i6a));
		pvec.push_back(port_t(PORT_PROTO_TCP, 80));
		pvec.push_back(port_t(PORT_PROTO_TCP, 443));
		pvec.push_back(port_t(PORT_PROTO_TCP, 143));
		pvec.push_back(port_t(PORT_PROTO_TCP, 22));
		pvec.push_back(port_t(PORT_PROTO_TCP, 139));
		pvec.push_back(port_t(PORT_PROTO_TCP, 31336));
		pvec.push_back(port_t(PORT_PROTO_TCP, 15));

		message_t msg(avec, pvec);

		if (X509_V_OK != ::SSL_get_verify_result(ssl)) 
			std::cout << "Certificate validation failed" << std::endl;
		else
			std::cout << "Certificate successfully validated" << std::endl;

		std::cout << "Connected with " << ::SSL_get_cipher(ssl) << " encryption." << std::endl;
	
		if (nullptr == ccert) {
			std::cerr << "ccert is nullptr" << std::endl;
			return EXIT_FAILURE;
		}

		line = ::X509_NAME_oneline(::X509_get_subject_name(ccert), 0, 0);
		std::cout << "Subject: " << line << std::endl;
		::free(line);
		line = ::X509_NAME_oneline(::X509_get_issuer_name(ccert), 0, 0); 
		std::cout << "Issuer: " << line << std::endl;
		::free(line);
		std::cout << "Version: " << ::X509_get_version(ccert) << std::endl;
		::X509_free(ccert);

		len = ::SSL_read(ssl, buf, sizeof(buf));

		if (0 < len && 4096 > len) {
			buf[len] = 0;
			std::cout << "buf: " << buf << std::endl;
		}

		std::vector< uint8_t > d(msg.data());
		::SSL_write(ssl, d.data(), d.size());
	}

	::close(sockfd);
	::SSL_CTX_free(ctx);
	return EXIT_SUCCESS;	
}