Ejemplo n.º 1
0
   void wallet_db::add_account( const account_record& blockchain_account,
                                const variant& private_data  )
   { try {
      wallet_account_record war;
      account_record& tmp  = war;
      tmp = blockchain_account;
      war.private_data = private_data;
      war.account_address = address(blockchain_account.owner_key);

      war.wallet_record_index = new_wallet_record_index();
      if (has_private_key(war.account_address))
          war.is_my_account = true;
      store_record( war );

      auto current_key = lookup_key( blockchain_account.owner_key );
      if( current_key.valid() )
      {  
         current_key->account_address = address(blockchain_account.owner_key);
         store_record( *current_key );
      }
      else
      {
         wallet_key_record new_key;
         new_key.wallet_record_index = new_wallet_record_index();
         new_key.account_address = address(blockchain_account.owner_key);
         new_key.public_key = blockchain_account.active_key();
         my->load_key_record( new_key, false );
         store_key( new_key );
      }
   } FC_CAPTURE_AND_RETHROW( (blockchain_account) ) }
Ejemplo n.º 2
0
   void wallet_db::store_key( const key_data& key_to_store )
   {
      auto key_itr = keys.find( key_to_store.get_address() );
      if( key_itr != keys.end() )
      {
         key_data& old_data = key_itr->second;
         old_data = key_to_store;

         if( key_to_store.has_private_key())
         {
            auto oacct = lookup_account( key_to_store.account_address );
            FC_ASSERT(oacct.valid(), "expecting an account to existing at this point");
            oacct->is_my_account = true;
            store_record( *oacct );
            cache_account( *oacct );
            ilog( "WALLET: storing private key for ${key} under account '${account_name}' address: (${account})",
                  ("key",key_to_store.public_key)
                  ("account",key_to_store.account_address)
                 ("account_name",get_account_name(key_to_store.account_address)) );
         }
         else
         {
            /*
            ilog( "WALLET: storing public key ${key} under account named '${account_name}' address: (${account})",
                  ("key",key_to_store.public_key)
                  ("account",key_to_store.account_address)
                  ("account_name",get_account_name(key_to_store.account_address)) );
                  */
         }
         ilog( "storing key" );

         store_record( key_itr->second, true );
      }
      else
      {
         auto r = wallet_key_record( key_to_store, new_wallet_record_index() );
         store_record( keys[key_to_store.get_address()] = r, true );

         auto key = key_to_store.public_key;
         auto bts_addr = key_to_store.get_address();
         btc_to_bts_address[ address(key) ] = bts_addr;
         btc_to_bts_address[ address(pts_address(key,false,56) )] = bts_addr;
         btc_to_bts_address[ address(pts_address(key,true,56) ) ] = bts_addr;
         btc_to_bts_address[ address(pts_address(key,false,0) ) ] = bts_addr;
         btc_to_bts_address[ address(pts_address(key,true,0) )  ] = bts_addr;
         ilog( "indexing key ${k}", ("k",address(pts_address(key,false,56) )  ) );
         ilog( "indexing key ${k}", ("k",address(pts_address(key,true,56) )  ) );
      }
   }
Ejemplo n.º 3
0
Archivo: soa.c Proyecto: jelu/validns
static struct rr* soa_parse(char *name, long ttl, int type, char *s)
{
    struct rr_soa *rr = getmem(sizeof(*rr));
    long long i;

    rr->mname = extract_name(&s, "mname", 0);
    if (!rr->mname) return NULL;
    rr->rname = extract_name(&s, "rname", 0);
    if (!rr->rname) return NULL;
    i = extract_integer(&s, "serial", NULL);
    if (i < 0) return NULL;
    if (i > 4294967295UL) return bitch("serial is out of range");
    rr->serial = i;
    rr->refresh = extract_timevalue(&s, "refresh");
    if (rr->refresh < 0) return NULL;
    rr->retry = extract_timevalue(&s, "retry");
    if (rr->retry < 0) return NULL;
    rr->expire = extract_timevalue(&s, "expire");
    if (rr->expire < 0) return NULL;
    rr->minimum = extract_timevalue(&s, "minimum");
    if (rr->minimum < 0) return NULL;
    if (ttl < 0 && G.opt.soa_minttl_as_default_ttl) {
        ttl = rr->minimum;
    }
    if (*s) {
        return bitch("garbage after valid SOA data");
    }
    return store_record(type, name, ttl, rr);
}
Ejemplo n.º 4
0
static void handle_accept_ack(consensus_component* comp,void* data){
    accept_ack* msg = data;
    // if currently the node is not the leader, then it should ignore all the
    // accept ack, because that can must be the msg from previous view
    SYS_LOG(comp,"Node %d Handle Accept Ack From Node %u.\n",
            comp->node_id,msg->node_id);
    if(comp->my_role!=LEADER){
        goto handle_accept_ack_exit;
    }
    // the request has reached quorum
    if(view_stamp_comp(&msg->msg_vs,comp->highest_committed_vs)<=0){
        goto handle_accept_ack_exit;
    }
    db_key_type record_no = vstol(&msg->msg_vs);
    request_record* record_data = NULL;
    size_t data_size;
    retrieve_record(comp->db_ptr,sizeof(record_no),&record_no,&data_size,(void**)&record_data);
    if(record_data==NULL){
        SYS_LOG(comp,"Received Ack To Non-Exist Record %lu.\n",
                record_no);
        goto handle_accept_ack_exit;
    }
    update_record(record_data,msg->node_id);
    // we do not care about whether the update is successful, otherwise this can
    // be treated as a message loss
    store_record(comp->db_ptr,sizeof(record_no),&record_no,REQ_RECORD_SIZE(record_data),record_data);
handle_accept_ack_exit:
    try_to_execute(comp);
    return;
};
Ejemplo n.º 5
0
static void handle_missing_ack(consensus_component* comp,void* data){
    
    missing_ack* msg = data;
    request_record* origin = (request_record*)msg->data;
    SYS_LOG(comp,"Node %d Handle Missing Ack From Node %d.\n",
            comp->node_id,msg->node_id);
    if(view_stamp_comp(comp->highest_committed_vs,&msg->missing_vs)>=0){
        goto handle_missing_ack_exit;
    }else{
       db_key_type record_no = vstol(&msg->missing_vs);
       request_record* record_data = NULL;
       size_t data_size;
       retrieve_record(comp->db_ptr,sizeof(record_no),&record_no,&data_size,(void**)&record_data);

       if(record_data!=NULL){
           goto handle_missing_ack_exit;
       }

       record_data =(request_record*)malloc(REQ_RECORD_SIZE(origin));

       if(record_data==NULL){
           goto handle_missing_ack_exit;
       }

       gettimeofday(&record_data->created_time,NULL);
       record_data->data_size = origin->data_size;
       memcpy(record_data->data,origin->data,origin->data_size);
       store_record(comp->db_ptr,sizeof(record_no),&record_no,REQ_RECORD_SIZE(record_data),record_data);
    }
    try_to_execute(comp);
handle_missing_ack_exit:
    
    return;
};
Ejemplo n.º 6
0
 void wallet_db::store_setting(const string& name, const variant& value)
 {
     auto orec = lookup_setting(name);
     if (orec.valid())
     {
         orec->value = value;
         settings[name] = *orec;
         store_record( *orec );
     }
     else
     {
         auto rec = wallet_setting_record( setting(name, value), new_wallet_record_index() );
         settings[name] = rec;
         store_record( rec );
     }
 }
Ejemplo n.º 7
0
 void wallet_db::set_master_key( const extended_private_key& extended_key,
                                  const fc::sha512& new_password )
 {
    master_key key;
    key.encrypt_key(new_password,extended_key);
    auto key_record = wallet_master_key_record( key, -1 );
    store_record( key_record, true );
 }
Ejemplo n.º 8
0
   void wallet_db::store_transaction( wallet_transaction_record& trx_to_store )
   { try {
      if( trx_to_store.wallet_record_index == 0 )
         trx_to_store.wallet_record_index = new_wallet_record_index();
      store_record( trx_to_store );

      transactions[ trx_to_store.record_id ] = trx_to_store;
   } FC_RETHROW_EXCEPTIONS( warn, "", ("trx_to_store",trx_to_store) ) }
Ejemplo n.º 9
0
 void wallet_db::cache_account( const wallet_account_record& war )
 {
    accounts[war.wallet_record_index] = war;
    if( war.id != 0 )
    {
       account_id_to_wallet_record_index[war.id] = war.wallet_record_index;
       name_to_account_wallet_record_index[war.name] = war.wallet_record_index;
    }
    store_record( war );
 }
Ejemplo n.º 10
0
 void wallet_db::update_market_order( const address& owner,
                                      const optional<bts::blockchain::market_order>& order,
                                      const transaction_id_type& trx_id )
 {
    if( order.valid() ) market_orders[ owner ].order = *order;
    else market_orders[ owner ].order.state.balance = 0;
    if( trx_id != transaction_id_type() )
       market_orders[ owner ].transactions.insert( trx_id );
    store_record( market_orders[ owner ] );
 }
Ejemplo n.º 11
0
   void wallet_db::add_account( const string& new_account_name,
                                const public_key_type& new_account_key,
                                const variant& private_data )
   {
      auto current_account_itr = name_to_account_wallet_record_index.find( new_account_name );
      FC_ASSERT( current_account_itr == name_to_account_wallet_record_index.end(),
                 "Account with name ${name} already exists",
                 ("name",new_account_name) );
      auto current_address_itr = address_to_account_wallet_record_index.find( new_account_key );
      FC_ASSERT( current_address_itr == address_to_account_wallet_record_index.end(),
                 "Account with address ${address} already exists",
                 ("name",new_account_key) );

      wallet_account_record war;
      war.name = new_account_name;
      war.id = 0;
      war.account_address = address( new_account_key );
      war.owner_key = new_account_key;
      war.set_active_key( blockchain::now(), new_account_key );
      war.private_data = private_data;

      war.wallet_record_index = new_wallet_record_index();
      if (has_private_key(war.account_address))
          war.is_my_account = true;
      store_record( war );

      auto current_key = lookup_key( new_account_key );
      if( current_key )
      {
         current_key->account_address = address(new_account_key);
         store_record( *current_key, true );
      }
      else
      {
         wallet_key_record new_key;
         new_key.wallet_record_index = new_wallet_record_index();
         new_key.account_address = address(new_account_key);
         new_key.public_key = new_account_key;
         my->load_key_record( new_key, false );
         store_key( new_key );
      }
   }
Ejemplo n.º 12
0
static struct rr* nsap_parse(char *name, long ttl, int type, char *s)
{
	struct rr_nsap *rr = getmem(sizeof(*rr));

	rr->data = extract_hex_binary_data(&s, "NSAP data", EXTRACT_EAT_WHITESPACE);
	if (rr->data.length < 0)	return NULL;

	if (*s) {
		return bitch("garbage after valid NSAP data");
	}
	return store_record(type, name, ttl, rr);
}
Ejemplo n.º 13
0
Archivo: a.c Proyecto: umq/validns
static struct rr *a_parse(char *name, long ttl, int type, char *s)
{
	struct rr_a *rr = getmem(sizeof(*rr));

	if (extract_ipv4(&s, "IPv4 address", &rr->address) <= 0)
		return NULL;
	if (*s) {
		return bitch("garbage after valid A data");
	}

	return store_record(type, name, ttl, rr);
}
Ejemplo n.º 14
0
Archivo: mr.c Proyecto: MikeAT/validns
static struct rr *mr_parse(char *name, long ttl, int type, char *s)
{
	struct rr_mr *rr = getmem(sizeof(*rr));

	rr->newname = extract_name(&s, "newname", 0);
	if (!rr->newname)
		return NULL;
	if (*s) {
		return bitch("garbage after valid MR data");
	}

	return store_record(type, name, ttl, rr);
}
Ejemplo n.º 15
0
Archivo: dlv.c Proyecto: jelu/validns
static struct rr* dlv_parse(char *name, long ttl, int type, char *s)
{
    struct rr_dlv *rr = getmem(sizeof(*rr));
    int key_tag, algorithm, digest_type;

    key_tag = extract_integer(&s, "key tag", NULL);
    if (key_tag < 0)    return NULL;
    rr->key_tag = key_tag;

    algorithm = extract_algorithm(&s, "algorithm");
    if (algorithm == ALG_UNSUPPORTED)   return NULL;
    rr->algorithm = algorithm;

    digest_type = extract_integer(&s, "digest type", NULL);
    if (digest_type < 0)    return NULL;
    rr->digest_type = digest_type;

    rr->digest = extract_hex_binary_data(&s, "digest", EXTRACT_EAT_WHITESPACE);
    if (rr->digest.length < 0)  return NULL;

    switch (digest_type) {
    case 1:
        if (rr->digest.length != SHA1_BYTES) {
            return bitch("wrong SHA-1 digest length: %d bytes found, %d bytes expected", rr->digest.length, SHA1_BYTES);
        }
        break;
    case 2:
        if (rr->digest.length != SHA256_BYTES) {
            return bitch("wrong SHA-256 digest length: %d bytes found, %d bytes expected", rr->digest.length, SHA256_BYTES);
        }
        break;
    case 3:
        if (rr->digest.length != GOST_BYTES) {
            return bitch("wrong GOST R 34.11-94 digest length: %d bytes found, %d bytes expected", rr->digest.length, GOST_BYTES);
        }
        break;
    case 4:
        if (rr->digest.length != SHA384_BYTES) {
            return bitch("wrong SHA-384 digest length: %d bytes found, %d bytes expected", rr->digest.length, SHA384_BYTES);
        }
        break;
    default:
        return bitch("bad or unsupported digest type %d", digest_type);
    }

    if (*s) {
        return bitch("garbage after valid DLV data");
    }
    G.dnssec_active = 1;
    return store_record(type, name, ttl, rr);
}
Ejemplo n.º 16
0
static struct rr *naptr_parse(char *name, long ttl, int type, char *s)
{
	struct rr_naptr *rr = getmem(sizeof(*rr));
	int i;
	struct binary_data text;

	i = extract_integer(&s, "order");
	if (i < 0)
		return NULL;
	if (i >= 65536)
		return bitch("order range is not valid");
	rr->order = i;

	i = extract_integer(&s, "preference");
	if (i < 0)
		return NULL;
	if (i >= 65536)
		return bitch("preference range is not valid");
	rr->preference = i;

	text = extract_text(&s, "flags");
	if (text.length < 0)
		return NULL;
	for (i = 0; i < text.length; i++) {
		if (!isalnum(text.data[i])) {
			return bitch("flags contains illegal characters");
		}
	}
	rr->flags = text;

	text = extract_text(&s, "services");
	if (text.length < 0)
		return NULL;
	rr->services = text;

	text = extract_text(&s, "regexp");
	if (text.length < 0)
		return NULL;
	rr->regexp = text;

	rr->replacement = extract_name(&s, "replacement");
	if (!rr->replacement)
		return NULL;

	if (*s) {
		return bitch("garbage after valid NAPTR data");
	}

	return store_record(type, name, ttl, rr);
}
Ejemplo n.º 17
0
static struct rr *nid_parse(char *name, long ttl, int type, char *s)
{
	struct rr_nid *rr = getmem(sizeof(*rr));
	int preference;

	rr->preference = preference = extract_integer(&s, "NID preference", NULL);
	if (preference < 0)
		return NULL;
	if (extract_u64(&s, "NodeID", &rr->node_id) < 0)
		return NULL;

	if (*s) {
		return bitch("garbage after valid NID data");
	}

	return store_record(type, name, ttl, rr);
}
Ejemplo n.º 18
0
   void wallet_db::cache_balance( const bts::blockchain::balance_record& balance_to_cache )
   {
      auto balance_id = balance_to_cache.id();
      owallet_balance_record current_bal = lookup_balance(balance_id);
      wallet_balance_record balance_record;
      if( !current_bal.valid() )
      {
         balance_record = wallet_balance_record( balance_to_cache, new_wallet_record_index() );
      }
      else
      {
         *current_bal = balance_to_cache;
         balance_record = *current_bal;
      }

      store_record( balance_record );
   }
Ejemplo n.º 19
0
Archivo: rt.c Proyecto: jelu/validns
static struct rr *rt_parse(char *name, long ttl, int type, char *s)
{
    struct rr_rt *rr = getmem(sizeof(*rr));

    rr->preference = extract_integer(&s, "RT preference", NULL);
    if (rr->preference < 0)
        return NULL;

    rr->intermediate_host = extract_name(&s, "intermediate-host", 0);
    if (!rr->intermediate_host)
        return NULL;
    if (*s) {
        return bitch("garbage after valid RT data");
    }

    return store_record(type, name, ttl, rr);
}
Ejemplo n.º 20
0
Archivo: mx.c Proyecto: umq/validns
static struct rr *mx_parse(char *name, long ttl, int type, char *s)
{
	struct rr_mx *rr = getmem(sizeof(*rr));

	rr->preference = extract_integer(&s, "MX preference");
	if (rr->preference < 0)
		return NULL;
	/* XXX preference range check */
	rr->exchange = extract_name(&s, "MX exchange", 0);
	if (!rr->exchange)
		return NULL;
	if (*s) {
		return bitch("garbage after valid MX data");
	}

	return store_record(type, name, ttl, rr);
}
Ejemplo n.º 21
0
Archivo: l64.c Proyecto: MikeAT/validns
static struct rr *l64_parse(char *name, long ttl, int type, char *s)
{
	struct rr_l64 *rr = getmem(sizeof(*rr));
	int preference;

	rr->preference = preference = extract_integer(&s, "L64 preference");
	if (preference < 0)
		return NULL;
	if (extract_u64(&s, "Locator64", &rr->locator64) < 0)
		return NULL;

	if (*s) {
		return bitch("garbage after valid L64 data");
	}

	return store_record(type, name, ttl, rr);
}
Ejemplo n.º 22
0
 void wallet_db::set_property( property_enum property_id, const variant& v )
 {
    wallet_property_record property_record;
    auto property_itr = properties.find( property_id );
    if( property_itr != properties.end() )
    {
        property_record = property_itr->second;
        property_record.value = v;
    }
    else
    {
        if( property_id == property_enum::next_record_number )
            property_record = wallet_property_record( wallet_property(property_id, v), 1 );
        else
            property_record = wallet_property_record( wallet_property(property_id, v), new_wallet_record_index() );
    }
    store_record( property_record );
 }
Ejemplo n.º 23
0
Archivo: kx.c Proyecto: jelu/validns
static struct rr *kx_parse(char *name, long ttl, int type, char *s)
{
    struct rr_kx *rr = getmem(sizeof(*rr));

    rr->preference = extract_integer(&s, "KX preference", NULL);
    if (rr->preference < 0)
        return NULL;

    rr->exchanger = extract_name(&s, "KX exchanger", 0);
    if (!rr->exchanger)
        return NULL;

    if (*s) {
        return bitch("garbage after valid KX data");
    }

    return store_record(type, name, ttl, rr);
}
Ejemplo n.º 24
0
static struct rr *minfo_parse(char *name, long ttl, int type, char *s)
{
	struct rr_minfo *rr = getmem(sizeof(*rr));

	rr->rmailbx = extract_name(&s, "rmailbx", 0);
	if (!rr->rmailbx)
		return NULL;

	rr->emailbx = extract_name(&s, "emailbx", 0);
	if (!rr->emailbx)
		return NULL;

	if (*s) {
		return bitch("garbage after valid MINFO data");
	}

	return store_record(type, name, ttl, rr);
}
Ejemplo n.º 25
0
   void wallet_db::change_password( const fc::sha512& old_password,
                                    const fc::sha512& new_password )
   { try {
      FC_ASSERT( wallet_master_key );
      auto old_key = get_master_key( old_password );
      FC_ASSERT( old_key, "unable to change password because old password was invalid" );
      set_master_key( *old_key, new_password );

      for( auto key : keys )
      {
         if( key.second.has_private_key() )
         {
            auto priv_key = key.second.decrypt_private_key( old_password );
            key.second.encrypt_private_key( new_password, priv_key );
            store_record( key.second, true );
         }
      }
   } FC_CAPTURE_AND_RETHROW() }
Ejemplo n.º 26
0
/*================================================================
 * add_indi_no_cache -- Add new person to database
 *  does not insert into cache
 *  (used by import)
 * (no user interaction)
 *==============================================================*/
BOOLEAN
add_indi_no_cache (NODE indi)
{
	NODE node, name, refn, sex, body, famc, fams;
	STRING str, key;

	split_indi_old(indi, &name, &refn, &sex, &body, &famc, &fams);
	key = rmvat(nxref(indi));
	for (node = name; node; node = nsibling(node))
		add_name(nval(node), key);
	for (node = refn; node; node = nsibling(node))
		if (nval(node)) add_refn(nval(node), key);
	join_indi(indi, name, refn, sex, body, famc, fams);
	resolve_refn_links(indi);
	str = node_to_string(indi);
	store_record(key, str, strlen(str));
	stdfree(str);
	return TRUE;
}
Ejemplo n.º 27
0
Archivo: l32.c Proyecto: jelu/validns
static struct rr *l32_parse(char *name, long ttl, int type, char *s)
{
    struct rr_l32 *rr = getmem(sizeof(*rr));
    struct in_addr ipv4_like;
    int preference;

    rr->preference = preference = extract_integer(&s, "L32 preference", NULL);
    if (preference < 0)
        return NULL;
    if (extract_ipv4(&s, "Locator32", &ipv4_like) <= 0)
        return NULL;
    rr->locator32 = ipv4_like.s_addr;

    if (*s) {
        return bitch("garbage after valid L32 data");
    }

    return store_record(type, name, ttl, rr);
}
Ejemplo n.º 28
0
static int leader_handle_submit_req(struct consensus_component_t* comp,
        size_t data_size,void* data,view_stamp* vs){
    
    int ret = 1;
    view_stamp next = get_next_view_stamp(comp);
    if(NULL!=vs){
        vs->view_id = next.view_id;
        vs->req_id = next.req_id;
    }
    db_key_type record_no = vstol(&next);
    request_record* record_data = 
        (request_record*)malloc(data_size+sizeof(request_record));
    gettimeofday(&record_data->created_time,NULL);
    record_data->bit_map = (1<<comp->node_id);
    record_data->data_size = data_size;
    record_data->is_closed = 0;
    memcpy(record_data->data,data,data_size);
    if(store_record(comp->db_ptr,sizeof(record_no),&record_no,REQ_RECORD_SIZE(record_data),record_data)){
        goto handle_submit_req_exit;
    }
    ret = 0;
    view_stamp_inc(comp->highest_seen_vs);
    if(comp->group_size>1){
        accept_req* msg = build_accept_req(comp,REQ_RECORD_SIZE(record_data),record_data,&next);
        SYS_LOG(comp, "group_size > 1, sending out consensus msg.\n");
        if(NULL==msg){
            goto handle_submit_req_exit;
        }
        comp->uc(comp->my_node,ACCEPT_REQ_SIZE(msg),msg,-1);
        free(msg);
    }else{
        SYS_LOG(comp, "group_size <= 1, execute by myself.\n");
        try_to_execute(comp);
    }
handle_submit_req_exit: 
    // no need to care about database, every time we will override it.
    if(record_data!=NULL){
        free(record_data);
    }
    
    return ret;
}
Ejemplo n.º 29
0
static struct rr* cert_parse(char *name, long ttl, int type, char *s)
{
	struct rr_cert *rr = getmem(sizeof(*rr));
	int cert_type, key_tag, alg;

	cert_type = extract_certificate_type(&s, "certificate type");
	if (cert_type < 0)	return NULL;
	rr->type = cert_type;

	key_tag = extract_integer(&s, "key tag");
	if (key_tag < 0)	return NULL;
	if (key_tag > 65535)
		return bitch("bad key tag");
	rr->key_tag = key_tag;

	if (isdigit(*s)) {
		alg = extract_integer(&s, "algorithm");
		if (alg < 0)	return NULL;
		if (alg > 255)	return bitch("bad algorithm");
		if (alg != 0) {  /* 0 is just fine */
			if (algorithm_type(alg) == ALG_UNSUPPORTED)
				return bitch("bad algorithm %d", alg);
		}
	} else {
		alg = extract_algorithm(&s, "algorithm");
		if (alg == ALG_UNSUPPORTED)	return NULL;
	}
	rr->algorithm = alg;

	if (alg == 0 && key_tag != 0) {
		/* we might want to bitch here, but RFC says "SHOULD", so we don't */
	}

	rr->certificate = extract_base64_binary_data(&s, "certificate");
	if (rr->certificate.length < 0)	return NULL;
	/* TODO validate cert length based on algorithm */

	if (*s) {
		return bitch("garbage after valid CERT data");
	}
	return store_record(type, name, ttl, rr);
}
Ejemplo n.º 30
0
Archivo: tlsa.c Proyecto: jelu/validns
static struct rr* tlsa_smimea_parse(char *name, long ttl, int type, char *s)
{
    struct rr_tlsa_smimea *rr = getmem(sizeof(*rr));
    int cert_usage, selector, matching_type;

    cert_usage = extract_integer(&s, "certificate usage field", NULL);
    if (cert_usage < 0) return NULL;
    if (cert_usage > 3)
        return bitch("bad certificate usage field");
    rr->cert_usage = cert_usage;

    selector = extract_integer(&s, "selector field", NULL);
    if (selector < 0)   return NULL;
    if (selector > 1)
        return bitch("bad selector field");
    rr->selector = selector;

    matching_type = extract_integer(&s, "matching type field", NULL);
    if (matching_type < 0)  return NULL;
    if (matching_type > 2)
        return bitch("bad matching type field");
    rr->matching_type = matching_type;

    rr->association_data = extract_hex_binary_data(&s, "certificate association data", EXTRACT_EAT_WHITESPACE);
    if (rr->association_data.length < 0)    return NULL;
    switch (rr->matching_type) {
    case 1:
        if (rr->association_data.length != SHA256_BYTES)
            return bitch("bad SHA-256 hash length");
        break;
    case 2:
        if (rr->association_data.length != SHA512_BYTES)
            return bitch("bad SHA-512 hash length");
        break;
    }

    if (*s) {
        return bitch("garbage after valid %s data", type == T_TLSA ? "TLSA" : "SMIMEA");
    }
    return store_record(type, name, ttl, rr);
}