Line data Source code
1 : #include "fd_gui_config_parse.h"
2 :
3 : #include "../../ballet/utf8/fd_utf8.h"
4 :
5 : int
6 : fd_gui_config_parse_validator_info_check( uchar const * data,
7 : ulong sz,
8 : cJSON ** out_json,
9 0 : fd_pubkey_t * out_pubkey ) {
10 : /*
11 : pub struct ConfigKeys {
12 : #[cfg_attr(feature = "serde", serde(with = "short_vec"))]
13 : pub keys: Vec<(Pubkey, bool)>,
14 : }
15 :
16 : The memory layout of a ConfigProgram account is a bincode serialized
17 : ConfigKeys followed immediately by a stringified json object
18 : containing the desired info.
19 :
20 : The short_vec serialization format is a 1-3 bytes size field (where
21 : the highest bit in a given byte is a continuation bit) followed by
22 : serialized elements in the vector (in this case, each element is a
23 : 32byte pubkey followed by a 1byte bool. For our simple parser, we
24 : only need to consider vectors smaller than 128 elements.
25 :
26 : The JSON schema for a validator info object is the following
27 :
28 : {
29 : "name": "<validator name>",
30 : "website": "<website url>",
31 : "details": "<validator details>",
32 : "iconUrl": "<icon url>"
33 : }
34 :
35 : Since accounts are at most 10MB, we should be safely within cJSON's
36 : allocator limits.
37 : */
38 0 : ulong i = 0UL;
39 :
40 0 : #define CHECK( cond ) do { \
41 0 : if( FD_UNLIKELY( !(cond) ) ) { \
42 0 : return 0; \
43 0 : } \
44 0 : } while( 0 )
45 :
46 : /* CHECK that it is safe to read at least n more bytes assuming i is
47 : the current location. n is untrusted and could trigger overflow, so
48 : don't do i+n<=payload_sz */
49 0 : #define CHECK_LEFT( n ) CHECK( (n)<=(sz-i) )
50 :
51 0 : CHECK_LEFT( 1UL ); uchar ck_sz = FD_LOAD( uchar, data+i ); i++;
52 0 : if( FD_UNLIKELY( ck_sz!=2 ) ) return 0;
53 :
54 0 : struct __attribute__((packed, aligned(1))) config_keys {
55 0 : fd_pubkey_t pubkey;
56 0 : uchar is_signer;
57 0 : };
58 :
59 0 : struct config_keys * data_config_keys = (struct config_keys *)(data + i);
60 0 : CHECK_LEFT( (sizeof(fd_pubkey_t) + 1UL)*ck_sz ); i += (sizeof(fd_pubkey_t) + 1UL)*ck_sz;
61 0 : CHECK_LEFT( FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_MAX_SZ );
62 :
63 : /* First entry should be Va1idator1nfo111111111111111111111111111111 */
64 0 : uchar expected[ 32UL ] = { 0x07, 0x51, 0x97, 0x01, 0x74, 0x48, 0xf2, 0xac, 0x5d, 0xc2, 0x3c, 0x9e, 0xbc, 0x7a, 0xc7, 0x8c, 0x0a, 0x27, 0x25, 0x7a, 0xc6, 0x14, 0x45, 0x8d, 0xe0, 0xa4, 0xf1, 0x6f, 0x80, 0x00, 0x00, 0x00 };
65 0 : if( FD_UNLIKELY( memcmp( data_config_keys[0].pubkey.uc, expected, sizeof(fd_pubkey_t) ) || data_config_keys[0].is_signer ) ) return 0;
66 :
67 0 : CHECK_LEFT( sizeof(ulong) ); ulong json_str_sz = FD_LOAD( ulong, data+i ); i += sizeof(ulong);
68 :
69 0 : CHECK_LEFT( json_str_sz+1UL ); /* cJSON_ParseWithLengthOpts requires having byte after the JSON payload */
70 0 : cJSON * json = cJSON_ParseWithLengthOpts( (char *)(data+i), json_str_sz, NULL, 0 );
71 0 : if( FD_UNLIKELY( !json ) ) return 0;
72 :
73 0 : #undef CHECK
74 0 : #undef CHECK_LEFT
75 :
76 0 : *out_json = json;
77 0 : fd_memcpy( out_pubkey->uc, data_config_keys[1].pubkey.uc, sizeof(fd_pubkey_t) );
78 0 : return 1;
79 0 : }
80 :
81 : void
82 0 : fd_gui_config_parse_validator_info( cJSON * json, fd_gui_config_parse_info_t * node_info ) {
83 0 : const cJSON * name = cJSON_GetObjectItemCaseSensitive( json, "name" );
84 : /* cJSON guarantees name->valuestring is NULL terminated */
85 0 : int missing_name = !cJSON_IsString( name )
86 0 : || strlen(name->valuestring)>FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_NAME_SZ
87 0 : || !fd_cstr_printf_check( node_info->name, strlen(name->valuestring)+1UL, NULL, "%s", name->valuestring )
88 0 : || !fd_utf8_verify( node_info->name, strlen(node_info->name) );
89 0 : if( FD_UNLIKELY( missing_name ) ) node_info->name[ 0 ] = '\0';
90 :
91 0 : const cJSON * website = cJSON_GetObjectItemCaseSensitive( json, "website" );
92 0 : int missing_website = !cJSON_IsString( website )
93 0 : || strlen(website->valuestring)>FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_WEBSITE_SZ
94 0 : || !fd_cstr_printf_check( node_info->website, strlen(website->valuestring)+1UL, NULL, "%s", website->valuestring )
95 0 : || !fd_utf8_verify( node_info->website, strlen(node_info->website) );
96 0 : if( FD_UNLIKELY( missing_website ) ) node_info->website[ 0 ] = '\0';
97 :
98 0 : const cJSON * details = cJSON_GetObjectItemCaseSensitive( json, "details" );
99 0 : int missing_details = !cJSON_IsString( details )
100 0 : || strlen(details->valuestring)>FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_DETAILS_SZ
101 0 : || !fd_cstr_printf_check( node_info->details, strlen(details->valuestring)+1UL, NULL, "%s", details->valuestring )
102 0 : || !fd_utf8_verify( node_info->details, strlen(node_info->details) );
103 0 : if( FD_UNLIKELY( missing_details ) ) node_info->details[ 0 ] = '\0';
104 :
105 0 : const cJSON * icon_uri = cJSON_GetObjectItemCaseSensitive( json, "iconUrl" );
106 0 : int missing_icon_uri = !cJSON_IsString( icon_uri )
107 0 : || strlen(icon_uri->valuestring)>FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_ICON_URI_SZ
108 0 : || !fd_cstr_printf_check( node_info->icon_uri, strlen(icon_uri->valuestring)+1UL, NULL, "%s", icon_uri->valuestring )
109 0 : || !fd_utf8_verify( node_info->icon_uri, strlen(node_info->icon_uri) );
110 0 : if( FD_UNLIKELY( missing_icon_uri ) ) node_info->icon_uri[ 0 ] = '\0';
111 :
112 0 : const cJSON * keybase_username = cJSON_GetObjectItemCaseSensitive( json, "keybaseUsername" );
113 0 : int missing_keybase_username = !cJSON_IsString( keybase_username )
114 0 : || strlen(keybase_username->valuestring)>FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_KEYBASE_USERNAME_SZ
115 0 : || !fd_cstr_printf_check( node_info->keybase_username, strlen(keybase_username->valuestring)+1UL, NULL, "%s", keybase_username->valuestring )
116 0 : || !fd_utf8_verify( node_info->keybase_username, strlen(node_info->keybase_username) );
117 0 : if( FD_UNLIKELY( missing_keybase_username ) ) node_info->keybase_username[ 0 ] = '\0';
118 :
119 0 : cJSON_Delete( json );
120 0 : }
|