Line data Source code
1 : #include "fd_contact_info.h" 2 : #include <string.h> 3 : 4 : static void 5 0 : reset_socket_tag_idx( ushort * socket_tag_idx ) { 6 0 : for( ulong i = 0UL; i<FD_GOSSIP_SOCKET_TAG_MAX; i++ ) { 7 0 : socket_tag_idx[ i ] = USHORT_MAX; 8 0 : } 9 0 : } 10 : 11 : static void 12 0 : refresh_metadata( fd_contact_info_t * ci_int ) { 13 0 : ushort cur_port = 0U; 14 0 : reset_socket_tag_idx( ci_int->socket_tag_idx ); 15 0 : for( ushort i = 0UL; i<ci_int->ci_crd.sockets_len; i++ ) { 16 0 : cur_port = (ushort)( cur_port + ci_int->sockets[ i ].offset ); 17 : 18 0 : ci_int->socket_tag_idx[ ci_int->sockets[ i ].key ] = i; 19 0 : ci_int->ports[ i ] = cur_port; 20 0 : } 21 0 : } 22 : 23 : ushort 24 0 : fd_contact_info_get_shred_version( fd_contact_info_t const * contact_info ) { 25 0 : return contact_info->ci_crd.shred_version; 26 0 : } 27 : 28 : void 29 : fd_contact_info_set_shred_version( fd_contact_info_t * contact_info, 30 0 : ushort shred_version ) { 31 0 : contact_info->ci_crd.shred_version = shred_version; 32 0 : } 33 : 34 : 35 : void 36 0 : fd_contact_info_init( fd_contact_info_t * contact_info ) { 37 0 : memset( contact_info, 0, sizeof(fd_contact_info_t) ); 38 : 39 0 : contact_info->ci_crd.addrs = contact_info->addrs; 40 0 : contact_info->ci_crd.sockets = contact_info->sockets; 41 : 42 0 : reset_socket_tag_idx( contact_info->socket_tag_idx ); 43 0 : } 44 : 45 : void 46 : fd_contact_info_from_ci_v2( fd_gossip_contact_info_v2_t const * ci_v2, 47 0 : fd_contact_info_t * contact_info ) { 48 0 : fd_gossip_contact_info_v2_t * ci_int = &contact_info->ci_crd; 49 0 : *ci_int = *ci_v2; 50 : 51 0 : ci_int->addrs = contact_info->addrs; 52 0 : ci_int->addrs_len = 0U; 53 0 : ci_int->sockets = contact_info->sockets; 54 0 : ci_int->sockets_len = 0U; 55 0 : ci_int->extensions = NULL; /* unsupported */ 56 0 : ci_int->extensions_len = 0U; /* unsupported */ 57 : 58 0 : reset_socket_tag_idx( contact_info->socket_tag_idx ); 59 : 60 : 61 : /* For sockets, validate individual entries and keep track of offsets */ 62 0 : ushort cur_offset = 0U; 63 0 : ushort cur_port = 0U; 64 0 : for( ulong i = 0UL; i<ci_v2->sockets_len; i++ ) { 65 0 : fd_gossip_socket_entry_t const * socket_entry = &ci_v2->sockets[ i ]; 66 0 : cur_offset = (ushort)( cur_offset + socket_entry->offset ); 67 0 : cur_port = (ushort)( cur_port + socket_entry->offset ); 68 : 69 0 : if( FD_UNLIKELY( socket_entry->key >= FD_GOSSIP_SOCKET_TAG_MAX ) ){ 70 : /* We seem to receive quite a few of these in testnet, so commented out logging 71 : TODO: Check with Anza about why this is the case */ 72 : // FD_LOG_WARNING(( "Invalid socket entry key %u", socket_entry->key )); 73 0 : continue; 74 0 : } 75 : 76 0 : if( FD_UNLIKELY( contact_info->socket_tag_idx[ socket_entry->key ]!=USHORT_MAX ) ){ 77 0 : FD_LOG_WARNING(( "Duplicate socket tag %u", socket_entry->key )); 78 0 : continue; 79 0 : } 80 : 81 : /* Find addr index 82 : TODO: can avoid nested for loop with a simple mapping of (ci_v2 addr_idx, ci_int addr_idx) */ 83 0 : uchar addr_index = UCHAR_MAX; 84 0 : for( ulong j = 0UL; j < ci_int->addrs_len; j++ ) { 85 0 : if( FD_LIKELY( memcmp(&ci_int->addrs[j], &ci_v2->addrs[socket_entry->index], sizeof(fd_gossip_ip_addr_t)) == 0 ) ) { 86 0 : addr_index = (uchar)j; 87 0 : break; 88 0 : } 89 0 : } 90 : 91 : /* Add entry to end of addrs if does not exist */ 92 0 : if( FD_UNLIKELY( addr_index == UCHAR_MAX ) ) { 93 0 : if( FD_UNLIKELY( socket_entry->index >= ci_v2->addrs_len ) ) { 94 0 : FD_LOG_WARNING(( "addr index %u out of bounds for addrs_len %u", socket_entry->index, ci_v2->addrs_len )); 95 0 : continue; 96 0 : } 97 0 : if( FD_UNLIKELY( ci_int->addrs_len >= FD_GOSSIP_SOCKET_TAG_MAX ) ) { 98 0 : FD_LOG_ERR(( "Too many unique addresses (%u) in contact info, possible broken implementation of fd_contact_info_from_ci_v2", ci_int->addrs_len )); 99 0 : continue; 100 0 : } 101 0 : ci_int->addrs[ ci_int->addrs_len ] = ci_v2->addrs[ socket_entry->index ]; 102 0 : addr_index = (uchar)ci_int->addrs_len; 103 0 : ci_int->addrs_len++; 104 0 : } 105 : 106 0 : ci_int->sockets[ ci_int->sockets_len ].index = addr_index; 107 0 : ci_int->sockets[ ci_int->sockets_len ].key = socket_entry->key; 108 0 : ci_int->sockets[ ci_int->sockets_len ].offset = cur_offset; 109 : 110 : /* Metadata updates */ 111 0 : contact_info->socket_tag_idx[ socket_entry->key ] = ci_int->sockets_len; 112 0 : contact_info->ports[ ci_int->sockets_len ] = cur_port; 113 : 114 0 : ci_int->sockets_len++; 115 0 : cur_offset = 0U; 116 0 : } 117 : 118 0 : if( FD_UNLIKELY( ci_int->sockets_len > FD_GOSSIP_SOCKET_TAG_MAX ) ){ 119 0 : FD_LOG_ERR(( "Too many sockets (%u) in contact info, possible broken implementation of fd_contact_info_from_ci_v2", ci_int->sockets_len )); 120 0 : } 121 0 : } 122 : 123 : void 124 : fd_contact_info_to_ci_v2( fd_contact_info_t const * ci_int, 125 0 : fd_gossip_contact_info_v2_t * ci_v2 ){ 126 0 : *ci_v2 = ci_int->ci_crd; 127 0 : } 128 : 129 : void 130 : fd_contact_info_to_update_msg( fd_contact_info_t const * contact_info, 131 0 : fd_gossip_update_msg_t * update ) { 132 0 : fd_gossip_contact_info_v2_t const * ci_v2 = &contact_info->ci_crd; 133 0 : fd_memcpy( update->pubkey, &ci_v2->from, sizeof(fd_pubkey_t) ); 134 : 135 0 : update->wallclock = ci_v2->wallclock; 136 0 : update->shred_version = ci_v2->shred_version; 137 0 : update->version_major = ci_v2->version.major; 138 0 : update->version_minor = ci_v2->version.minor; 139 0 : update->version_patch = ci_v2->version.patch; 140 0 : update->version_commit = ci_v2->version.commit; 141 0 : update->version_feature_set = ci_v2->version.feature_set; 142 : 143 : /* TODO: missing version_commit_type and version_type */ 144 0 : update->version_commit_type = 0U; 145 0 : update->version_type = 0U; 146 : 147 0 : ushort cur_port = 0U; 148 0 : for( ulong i = 0UL; i<FD_GOSSIP_SOCKET_TAG_MAX; i++ ) { 149 0 : fd_gossip_socket_entry_t const * socket_entry = &ci_v2->sockets[ i ]; 150 0 : cur_port = (ushort)( cur_port + socket_entry->offset ); 151 0 : ushort socket_tag = socket_entry->key; 152 : 153 : /* NOTE: We use FD_GOSSIP_UPDATE_MSG_NUM_SOCKETS instead of FD_GOSSIP_SOCKET_TAG_MAX 154 : since they aren't strictly the same. */ 155 : 156 0 : if( FD_UNLIKELY( socket_tag >= FD_GOSSIP_UPDATE_MSG_NUM_SOCKETS ) ){ 157 0 : FD_LOG_WARNING(( "Unsupported socket tag in update msg %u", socket_tag )); 158 0 : continue; 159 0 : } 160 0 : if( FD_UNLIKELY( !fd_gossip_ip_addr_is_ip4( &ci_v2->addrs[ socket_entry->index ] ))){ 161 : /* Skip non IPv4 entries */ 162 0 : continue; 163 0 : } 164 : 165 0 : update->addrs[ socket_tag ].ip = ci_v2->addrs[ socket_entry->index ].inner.ip4; 166 0 : update->addrs[ socket_tag ].port = cur_port ; 167 0 : } 168 0 : } 169 : 170 : int 171 : fd_contact_info_get_socket_addr( fd_contact_info_t const * ci_int, 172 : uchar socket_tag, 173 0 : fd_gossip_socket_addr_t * out_addr ) { 174 0 : if( FD_UNLIKELY( socket_tag >= FD_GOSSIP_SOCKET_TAG_MAX ) ) { 175 0 : FD_LOG_ERR(( "Invalid socket tag %u", socket_tag )); 176 0 : return -1; 177 0 : } 178 0 : if( FD_UNLIKELY( ci_int->socket_tag_idx[ socket_tag ]==USHORT_MAX ) ) { 179 0 : FD_LOG_WARNING(( "Socket tag %u not found in contact info", socket_tag )); 180 0 : return -1; 181 0 : } 182 0 : ushort socket_idx = ci_int->socket_tag_idx[ socket_tag ]; 183 0 : fd_gossip_socket_entry_t const * socket_entry = &ci_int->ci_crd.sockets[ socket_idx ]; 184 0 : fd_gossip_ip_addr_t const * addr = &ci_int->ci_crd.addrs[ socket_entry->index ]; 185 0 : ushort port = fd_ushort_bswap( ci_int->ports[ socket_idx ] ); 186 : 187 0 : if( FD_LIKELY( fd_gossip_ip_addr_is_ip4( addr ) ) ) { 188 0 : fd_gossip_socket_addr_new_disc( out_addr, fd_gossip_socket_addr_enum_ip4 ); 189 0 : out_addr->inner.ip4.port = port; 190 0 : out_addr->inner.ip4.addr = addr->inner.ip4; 191 0 : } else { 192 0 : fd_gossip_socket_addr_new_disc( out_addr, fd_gossip_socket_addr_enum_ip6 ); 193 0 : out_addr->inner.ip6.port = port; 194 0 : out_addr->inner.ip6.addr = addr->inner.ip6; 195 0 : } 196 : 197 0 : return 0; 198 : 199 0 : } 200 : 201 : static int 202 : fd_contact_info_remove_socket( fd_contact_info_t * ci_int, 203 0 : ushort socket_tag ) { 204 0 : if( FD_UNLIKELY( socket_tag >= FD_GOSSIP_SOCKET_TAG_MAX ) ){ 205 0 : FD_LOG_ERR(( "Invalid socket tag %u", socket_tag )); 206 0 : } 207 : 208 0 : if( FD_UNLIKELY( ci_int->socket_tag_idx[ socket_tag ]==USHORT_MAX ) ) { 209 0 : FD_LOG_WARNING(( "Socket tag %u not found in contact info", socket_tag )); 210 0 : return -1; 211 0 : } 212 : 213 0 : ushort socket_idx = ci_int->socket_tag_idx[ socket_tag ]; 214 0 : ushort addr_idx = ci_int->ci_crd.sockets[ socket_idx ].index; 215 0 : memmove( &ci_int->ci_crd.sockets[ socket_idx ], 216 0 : &ci_int->ci_crd.sockets[ socket_idx+1 ], 217 0 : sizeof(fd_gossip_socket_entry_t)*(ulong)( ci_int->ci_crd.sockets_len - socket_idx - 1 ) ); 218 0 : ci_int->ci_crd.sockets_len--; 219 : 220 : /* Remove addr idx if no longer in any socket entry */ 221 0 : int addr_found = 0; 222 0 : for( ulong i = 0UL; i<ci_int->ci_crd.sockets_len; i++ ) { 223 0 : if( ci_int->ci_crd.sockets[ i ].index == addr_idx ){ 224 0 : addr_found = 1; 225 0 : break; 226 0 : } 227 0 : } 228 : 229 0 : if( !addr_found ){ 230 0 : memmove( &ci_int->ci_crd.addrs[ addr_idx ], 231 0 : &ci_int->ci_crd.addrs[ addr_idx+1 ], 232 0 : sizeof(fd_gossip_ip_addr_t)*(ulong)( ci_int->ci_crd.addrs_len - addr_idx - 1 ) ); 233 0 : ci_int->ci_crd.addrs_len--; 234 0 : } 235 : 236 0 : refresh_metadata( ci_int ); 237 : 238 0 : return 0; 239 0 : } 240 : 241 : int 242 : fd_contact_info_insert_socket( fd_contact_info_t * ci_int, 243 : fd_gossip_peer_addr_t const * peer, 244 0 : uchar socket_tag ) { 245 0 : if( FD_UNLIKELY( socket_tag >= FD_GOSSIP_SOCKET_TAG_MAX ) ) { 246 0 : FD_LOG_ERR(( "Invalid socket tag %u", socket_tag )); 247 0 : } 248 : 249 0 : if( FD_UNLIKELY( ci_int->socket_tag_idx[ socket_tag ]!=FD_CONTACT_INFO_SOCKET_TAG_NULL ) ) { 250 0 : FD_LOG_NOTICE(( "Overwriting socket tag %u", socket_tag )); 251 0 : fd_contact_info_remove_socket( ci_int, socket_tag ); 252 0 : } 253 : 254 0 : ushort new_port = fd_ushort_bswap( peer->port ); 255 0 : fd_gossip_socket_entry_t new_socket_entry; 256 0 : new_socket_entry.key = socket_tag; 257 : 258 : /* Find idx to insert in */ 259 0 : ushort insert_idx = 0; 260 0 : ushort cur_port = 0U; 261 0 : for( ; insert_idx<ci_int->ci_crd.sockets_len; insert_idx++ ) { 262 0 : fd_gossip_socket_entry_t const * socket_entry = &ci_int->sockets[ insert_idx ]; 263 0 : if( FD_LIKELY( cur_port + socket_entry->offset > new_port ) ) { 264 0 : break; 265 0 : } 266 0 : cur_port = (ushort)( cur_port + socket_entry->offset ); 267 0 : } 268 : 269 0 : new_socket_entry.offset = (ushort)( new_port - cur_port ); 270 : 271 : /* Update the offset for the entry currently in insert_idx */ 272 0 : ci_int->sockets[ insert_idx ].offset = (ushort)( ci_int->sockets[ insert_idx ].offset - new_socket_entry.offset ); 273 : 274 : /* Shift all entries starting from insert_idx down */ 275 0 : memmove( &ci_int->sockets[ insert_idx+1 ], 276 0 : &ci_int->sockets[ insert_idx ], 277 0 : sizeof(fd_gossip_socket_entry_t)*(ulong)( ci_int->ci_crd.sockets_len - insert_idx ) ); 278 0 : ci_int->ci_crd.sockets_len++; 279 : 280 : /* Find addr idx */ 281 0 : uchar addr_idx = 0U; 282 0 : for( ; addr_idx<ci_int->ci_crd.addrs_len; addr_idx++ ) { 283 0 : if( FD_LIKELY( ci_int->ci_crd.addrs[ addr_idx ].inner.ip4==peer->addr ) ) { 284 0 : break; 285 0 : } 286 0 : } 287 : 288 0 : if( FD_UNLIKELY( addr_idx==ci_int->ci_crd.addrs_len ) ) { 289 0 : FD_LOG_INFO(( "Adding new addr %u", peer->addr ) ); 290 0 : fd_gossip_ip_addr_new_disc( &ci_int->ci_crd.addrs[ addr_idx ], fd_gossip_ip_addr_enum_ip4 ); 291 0 : ci_int->ci_crd.addrs[ addr_idx ].inner.ip4 = peer->addr; 292 0 : ci_int->ci_crd.addrs_len++; 293 0 : } 294 : 295 0 : new_socket_entry.index = addr_idx; 296 0 : ci_int->ci_crd.sockets[ insert_idx ] = new_socket_entry; 297 : 298 : /* Refresh metadata */ 299 0 : refresh_metadata( ci_int ); 300 0 : return 0; 301 0 : } 302 : 303 : int 304 : fd_gossip_contact_info_v2_find_proto_ident( fd_gossip_contact_info_v2_t const * contact_info, 305 : uchar proto_ident, 306 0 : fd_gossip_socket_addr_t * out_addr ) { 307 0 : ushort port = 0; 308 0 : for( ulong i = 0UL; i<contact_info->sockets_len; i++ ) { 309 0 : fd_gossip_socket_entry_t const * socket_entry = &contact_info->sockets[ i ]; 310 0 : port = (ushort)( port + socket_entry->offset ); 311 0 : if( socket_entry->key==proto_ident ) { 312 0 : if( socket_entry->index>=contact_info->addrs_len) { 313 0 : continue; 314 0 : } 315 : 316 : /* fd_gossip_socket_addr->inner and fd_gossip_ip_addr 317 : are slightly different, so we can't just 318 : out_addr->ip = contact_info->addrs[ idx ] */ 319 0 : fd_gossip_ip_addr_t * tmp = &contact_info->addrs[ socket_entry->index ]; 320 0 : if( FD_LIKELY( tmp->discriminant == fd_gossip_ip_addr_enum_ip4 ) ) { 321 0 : out_addr->discriminant = fd_gossip_socket_addr_enum_ip4; 322 0 : out_addr->inner.ip4.addr = tmp->inner.ip4; 323 0 : out_addr->inner.ip4.port = port; 324 0 : } else { 325 0 : out_addr->discriminant = fd_gossip_socket_addr_enum_ip6; 326 0 : out_addr->inner.ip6.addr = tmp->inner.ip6; 327 0 : out_addr->inner.ip6.port = port; 328 0 : } 329 0 : return 1; 330 0 : } 331 0 : } 332 : 333 0 : return 0; 334 0 : }