@@ -824,6 +824,8 @@ struct accept_info {
824824 char * origin ;
825825 int has_is_physrep ;
826826 int is_physrep ;
827+ struct accept_info * hostcheck_next ; /* hostcheck thread queue link */
828+ int hostcheck_ok ; /* result of the peer-hostname check */
827829};
828830
829831static int pending_connections ; /* accepted, but not processed first-byte */
@@ -2564,6 +2566,80 @@ static void net_accept_ssl_error(void *data)
25642566 accept_info_free (a );
25652567}
25662568
2569+ /* Continue accepting a connection once the peer's claimed hostname has been
2570+ verified against its source address (see validate_host / hostcheck_fn).
2571+ Runs on the main event base. Returns 0 on success (or once SSL setup has
2572+ been handed off), -1 to reject. */
2573+ static int validate_host_finish (struct accept_info * a )
2574+ {
2575+ check_base_thd ();
2576+ if (a -> c .flags & CONNECT_MSG_SSL ) {
2577+ if (!SSL_IS_ABLE (gbl_rep_ssl_mode )) {
2578+ logmsg (LOGMSG_ERROR , "Peer requested SSL, but I don't have an SSL key pair.\n" );
2579+ return -1 ;
2580+ }
2581+ a -> origin = get_hostname_by_fileno (a -> fd );
2582+ a -> ssl_data = ssl_data_new (a -> fd , a -> origin );
2583+ accept_ssl_evbuffer (a -> ssl_data , base , net_accept_ssl_error , net_accept_ssl_success , a );
2584+ return 0 ;
2585+ } else if (SSL_IS_REQUIRED (gbl_rep_ssl_mode )) {
2586+ logmsg (LOGMSG_ERROR , "Replicant SSL connections are required.\n" );
2587+ return -1 ;
2588+ }
2589+ return accept_host (a );
2590+ }
2591+
2592+ /* Verifying that a peer's source address matches its claimed hostname can
2593+ require blocking DNS resolution (getaddrinfo), which must not run on the
2594+ event loop. Connections that need the check are handed to a dedicated
2595+ thread, which resolves the hostname and then resumes acceptance back on the
2596+ main event base - mirroring newsql's gethostname thread. */
2597+ extern int gbl_rep_verify_peer_hostname ;
2598+
2599+ static pthread_t hostcheck_thd ;
2600+ static pthread_mutex_t hostcheck_lk = PTHREAD_MUTEX_INITIALIZER ;
2601+ static pthread_cond_t hostcheck_cond = PTHREAD_COND_INITIALIZER ;
2602+ static struct accept_info * hostcheck_head ;
2603+ static struct accept_info * hostcheck_tail ;
2604+
2605+ /* Back on the event base: act on the resolver's verdict. */
2606+ static void hostcheck_resume (int dummyfd , short what , void * data )
2607+ {
2608+ struct accept_info * a = data ;
2609+ if (!a -> hostcheck_ok ) {
2610+ logmsg (LOGMSG_ERROR , "%s fd:%d rejecting connection from node:%d host:%s: source address does not match\n" ,
2611+ __func__ , a -> fd , a -> c .from_nodenum , a -> from_host_interned );
2612+ accept_info_free (a );
2613+ return ;
2614+ }
2615+ if (validate_host_finish (a ) != 0 ) {
2616+ accept_info_free (a );
2617+ }
2618+ }
2619+
2620+ /* Dedicated thread: forward-resolve claimed hostnames off the event loop. */
2621+ static void * hostcheck_fn (void * unused )
2622+ {
2623+ comdb2_name_thread ("hostcheck" );
2624+ while (1 ) {
2625+ Pthread_mutex_lock (& hostcheck_lk );
2626+ while (hostcheck_head == NULL ) {
2627+ Pthread_cond_wait (& hostcheck_cond , & hostcheck_lk );
2628+ }
2629+ struct accept_info * a = hostcheck_head ;
2630+ hostcheck_head = a -> hostcheck_next ;
2631+ if (hostcheck_head == NULL ) {
2632+ hostcheck_tail = NULL ;
2633+ }
2634+ a -> hostcheck_next = NULL ;
2635+ Pthread_mutex_unlock (& hostcheck_lk );
2636+
2637+ a -> hostcheck_ok = (net_validate_connect_host (a -> from_host_interned , & a -> ss ) == 0 );
2638+ evtimer_once (base , hostcheck_resume , a ); /* resume on the main event base */
2639+ }
2640+ return NULL ;
2641+ }
2642+
25672643static int validate_host (struct accept_info * a )
25682644{
25692645 if (strcmp (a -> from_host , gbl_myhostname ) == 0 ) {
@@ -2626,20 +2702,24 @@ static int validate_host(struct accept_info *a)
26262702 return -1 ;
26272703 }
26282704 }
2629- if (a -> c .flags & CONNECT_MSG_SSL ) {
2630- if (!SSL_IS_ABLE (gbl_rep_ssl_mode )) {
2631- logmsg (LOGMSG_ERROR , "Peer requested SSL, but I don't have an SSL key pair.\n" );
2632- return -1 ;
2633- }
2634- a -> origin = get_hostname_by_fileno (a -> fd );
2635- a -> ssl_data = ssl_data_new (a -> fd , a -> origin );
2636- accept_ssl_evbuffer (a -> ssl_data , base , net_accept_ssl_error , net_accept_ssl_success , a );
2705+ /* Confirm the connection's source address really belongs to the claimed
2706+ hostname, so a rogue node can't impersonate a trusted peer just by
2707+ putting its hostname in the connect message. The check may block on DNS
2708+ resolution, so hand it to the hostcheck thread and resume acceptance in
2709+ validate_host_finish() once it completes. */
2710+ if (gbl_rep_verify_peer_hostname ) {
2711+ a -> hostcheck_next = NULL ;
2712+ Pthread_mutex_lock (& hostcheck_lk );
2713+ if (hostcheck_tail )
2714+ hostcheck_tail -> hostcheck_next = a ;
2715+ else
2716+ hostcheck_head = a ;
2717+ hostcheck_tail = a ;
2718+ Pthread_cond_signal (& hostcheck_cond ); /* -> hostcheck_fn */
2719+ Pthread_mutex_unlock (& hostcheck_lk );
26372720 return 0 ;
2638- } else if (SSL_IS_REQUIRED (gbl_rep_ssl_mode )) {
2639- logmsg (LOGMSG_ERROR , "Replicant SSL connections are required.\n" );
2640- return -1 ;
26412721 }
2642- return accept_host (a );
2722+ return validate_host_finish (a );
26432723}
26442724
26452725static int process_long_hostname (struct accept_info * a )
@@ -3590,6 +3670,7 @@ static void setup_bases(void)
35903670{
35913671 event_set_fatal_callback (libevent_fatal_cb );
35923672 init_base (& base_thd , & base , "main" , NULL );
3673+ Pthread_create (& hostcheck_thd , NULL , hostcheck_fn , NULL ); /* off-loads peer-hostname DNS checks */
35933674 if (dedicated_timer ) {
35943675 gettimeofday (& timer_tick , NULL );
35953676 init_base (& timer_thd , & timer_base , "timer" , & timer_tick );
0 commit comments