162306a36Sopenharmony_ci// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) 262306a36Sopenharmony_ci/* Do not edit directly, auto-generated from: */ 362306a36Sopenharmony_ci/* Documentation/netlink/specs/handshake.yaml */ 462306a36Sopenharmony_ci/* YNL-GEN user source */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <stdlib.h> 762306a36Sopenharmony_ci#include <string.h> 862306a36Sopenharmony_ci#include "handshake-user.h" 962306a36Sopenharmony_ci#include "ynl.h" 1062306a36Sopenharmony_ci#include <linux/handshake.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <libmnl/libmnl.h> 1362306a36Sopenharmony_ci#include <linux/genetlink.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* Enums */ 1662306a36Sopenharmony_cistatic const char * const handshake_op_strmap[] = { 1762306a36Sopenharmony_ci [HANDSHAKE_CMD_READY] = "ready", 1862306a36Sopenharmony_ci [HANDSHAKE_CMD_ACCEPT] = "accept", 1962306a36Sopenharmony_ci [HANDSHAKE_CMD_DONE] = "done", 2062306a36Sopenharmony_ci}; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ciconst char *handshake_op_str(int op) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci if (op < 0 || op >= (int)MNL_ARRAY_SIZE(handshake_op_strmap)) 2562306a36Sopenharmony_ci return NULL; 2662306a36Sopenharmony_ci return handshake_op_strmap[op]; 2762306a36Sopenharmony_ci} 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic const char * const handshake_handler_class_strmap[] = { 3062306a36Sopenharmony_ci [0] = "none", 3162306a36Sopenharmony_ci [1] = "tlshd", 3262306a36Sopenharmony_ci [2] = "max", 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ciconst char *handshake_handler_class_str(enum handshake_handler_class value) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci if (value < 0 || value >= (int)MNL_ARRAY_SIZE(handshake_handler_class_strmap)) 3862306a36Sopenharmony_ci return NULL; 3962306a36Sopenharmony_ci return handshake_handler_class_strmap[value]; 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic const char * const handshake_msg_type_strmap[] = { 4362306a36Sopenharmony_ci [0] = "unspec", 4462306a36Sopenharmony_ci [1] = "clienthello", 4562306a36Sopenharmony_ci [2] = "serverhello", 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ciconst char *handshake_msg_type_str(enum handshake_msg_type value) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci if (value < 0 || value >= (int)MNL_ARRAY_SIZE(handshake_msg_type_strmap)) 5162306a36Sopenharmony_ci return NULL; 5262306a36Sopenharmony_ci return handshake_msg_type_strmap[value]; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic const char * const handshake_auth_strmap[] = { 5662306a36Sopenharmony_ci [0] = "unspec", 5762306a36Sopenharmony_ci [1] = "unauth", 5862306a36Sopenharmony_ci [2] = "psk", 5962306a36Sopenharmony_ci [3] = "x509", 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ciconst char *handshake_auth_str(enum handshake_auth value) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci if (value < 0 || value >= (int)MNL_ARRAY_SIZE(handshake_auth_strmap)) 6562306a36Sopenharmony_ci return NULL; 6662306a36Sopenharmony_ci return handshake_auth_strmap[value]; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* Policies */ 7062306a36Sopenharmony_cistruct ynl_policy_attr handshake_x509_policy[HANDSHAKE_A_X509_MAX + 1] = { 7162306a36Sopenharmony_ci [HANDSHAKE_A_X509_CERT] = { .name = "cert", .type = YNL_PT_U32, }, 7262306a36Sopenharmony_ci [HANDSHAKE_A_X509_PRIVKEY] = { .name = "privkey", .type = YNL_PT_U32, }, 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistruct ynl_policy_nest handshake_x509_nest = { 7662306a36Sopenharmony_ci .max_attr = HANDSHAKE_A_X509_MAX, 7762306a36Sopenharmony_ci .table = handshake_x509_policy, 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistruct ynl_policy_attr handshake_accept_policy[HANDSHAKE_A_ACCEPT_MAX + 1] = { 8162306a36Sopenharmony_ci [HANDSHAKE_A_ACCEPT_SOCKFD] = { .name = "sockfd", .type = YNL_PT_U32, }, 8262306a36Sopenharmony_ci [HANDSHAKE_A_ACCEPT_HANDLER_CLASS] = { .name = "handler-class", .type = YNL_PT_U32, }, 8362306a36Sopenharmony_ci [HANDSHAKE_A_ACCEPT_MESSAGE_TYPE] = { .name = "message-type", .type = YNL_PT_U32, }, 8462306a36Sopenharmony_ci [HANDSHAKE_A_ACCEPT_TIMEOUT] = { .name = "timeout", .type = YNL_PT_U32, }, 8562306a36Sopenharmony_ci [HANDSHAKE_A_ACCEPT_AUTH_MODE] = { .name = "auth-mode", .type = YNL_PT_U32, }, 8662306a36Sopenharmony_ci [HANDSHAKE_A_ACCEPT_PEER_IDENTITY] = { .name = "peer-identity", .type = YNL_PT_U32, }, 8762306a36Sopenharmony_ci [HANDSHAKE_A_ACCEPT_CERTIFICATE] = { .name = "certificate", .type = YNL_PT_NEST, .nest = &handshake_x509_nest, }, 8862306a36Sopenharmony_ci [HANDSHAKE_A_ACCEPT_PEERNAME] = { .name = "peername", .type = YNL_PT_NUL_STR, }, 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistruct ynl_policy_nest handshake_accept_nest = { 9262306a36Sopenharmony_ci .max_attr = HANDSHAKE_A_ACCEPT_MAX, 9362306a36Sopenharmony_ci .table = handshake_accept_policy, 9462306a36Sopenharmony_ci}; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistruct ynl_policy_attr handshake_done_policy[HANDSHAKE_A_DONE_MAX + 1] = { 9762306a36Sopenharmony_ci [HANDSHAKE_A_DONE_STATUS] = { .name = "status", .type = YNL_PT_U32, }, 9862306a36Sopenharmony_ci [HANDSHAKE_A_DONE_SOCKFD] = { .name = "sockfd", .type = YNL_PT_U32, }, 9962306a36Sopenharmony_ci [HANDSHAKE_A_DONE_REMOTE_AUTH] = { .name = "remote-auth", .type = YNL_PT_U32, }, 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistruct ynl_policy_nest handshake_done_nest = { 10362306a36Sopenharmony_ci .max_attr = HANDSHAKE_A_DONE_MAX, 10462306a36Sopenharmony_ci .table = handshake_done_policy, 10562306a36Sopenharmony_ci}; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* Common nested types */ 10862306a36Sopenharmony_civoid handshake_x509_free(struct handshake_x509 *obj) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ciint handshake_x509_parse(struct ynl_parse_arg *yarg, 11362306a36Sopenharmony_ci const struct nlattr *nested) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct handshake_x509 *dst = yarg->data; 11662306a36Sopenharmony_ci const struct nlattr *attr; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci mnl_attr_for_each_nested(attr, nested) { 11962306a36Sopenharmony_ci unsigned int type = mnl_attr_get_type(attr); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (type == HANDSHAKE_A_X509_CERT) { 12262306a36Sopenharmony_ci if (ynl_attr_validate(yarg, attr)) 12362306a36Sopenharmony_ci return MNL_CB_ERROR; 12462306a36Sopenharmony_ci dst->_present.cert = 1; 12562306a36Sopenharmony_ci dst->cert = mnl_attr_get_u32(attr); 12662306a36Sopenharmony_ci } else if (type == HANDSHAKE_A_X509_PRIVKEY) { 12762306a36Sopenharmony_ci if (ynl_attr_validate(yarg, attr)) 12862306a36Sopenharmony_ci return MNL_CB_ERROR; 12962306a36Sopenharmony_ci dst->_present.privkey = 1; 13062306a36Sopenharmony_ci dst->privkey = mnl_attr_get_u32(attr); 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci return 0; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/* ============== HANDSHAKE_CMD_ACCEPT ============== */ 13862306a36Sopenharmony_ci/* HANDSHAKE_CMD_ACCEPT - do */ 13962306a36Sopenharmony_civoid handshake_accept_req_free(struct handshake_accept_req *req) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci free(req); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_civoid handshake_accept_rsp_free(struct handshake_accept_rsp *rsp) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci unsigned int i; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci free(rsp->peer_identity); 14962306a36Sopenharmony_ci for (i = 0; i < rsp->n_certificate; i++) 15062306a36Sopenharmony_ci handshake_x509_free(&rsp->certificate[i]); 15162306a36Sopenharmony_ci free(rsp->certificate); 15262306a36Sopenharmony_ci free(rsp->peername); 15362306a36Sopenharmony_ci free(rsp); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ciint handshake_accept_rsp_parse(const struct nlmsghdr *nlh, void *data) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci struct ynl_parse_arg *yarg = data; 15962306a36Sopenharmony_ci struct handshake_accept_rsp *dst; 16062306a36Sopenharmony_ci unsigned int n_peer_identity = 0; 16162306a36Sopenharmony_ci unsigned int n_certificate = 0; 16262306a36Sopenharmony_ci const struct nlattr *attr; 16362306a36Sopenharmony_ci struct ynl_parse_arg parg; 16462306a36Sopenharmony_ci int i; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci dst = yarg->data; 16762306a36Sopenharmony_ci parg.ys = yarg->ys; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (dst->certificate) 17062306a36Sopenharmony_ci return ynl_error_parse(yarg, "attribute already present (accept.certificate)"); 17162306a36Sopenharmony_ci if (dst->peer_identity) 17262306a36Sopenharmony_ci return ynl_error_parse(yarg, "attribute already present (accept.peer-identity)"); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci mnl_attr_for_each(attr, nlh, sizeof(struct genlmsghdr)) { 17562306a36Sopenharmony_ci unsigned int type = mnl_attr_get_type(attr); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (type == HANDSHAKE_A_ACCEPT_SOCKFD) { 17862306a36Sopenharmony_ci if (ynl_attr_validate(yarg, attr)) 17962306a36Sopenharmony_ci return MNL_CB_ERROR; 18062306a36Sopenharmony_ci dst->_present.sockfd = 1; 18162306a36Sopenharmony_ci dst->sockfd = mnl_attr_get_u32(attr); 18262306a36Sopenharmony_ci } else if (type == HANDSHAKE_A_ACCEPT_MESSAGE_TYPE) { 18362306a36Sopenharmony_ci if (ynl_attr_validate(yarg, attr)) 18462306a36Sopenharmony_ci return MNL_CB_ERROR; 18562306a36Sopenharmony_ci dst->_present.message_type = 1; 18662306a36Sopenharmony_ci dst->message_type = mnl_attr_get_u32(attr); 18762306a36Sopenharmony_ci } else if (type == HANDSHAKE_A_ACCEPT_TIMEOUT) { 18862306a36Sopenharmony_ci if (ynl_attr_validate(yarg, attr)) 18962306a36Sopenharmony_ci return MNL_CB_ERROR; 19062306a36Sopenharmony_ci dst->_present.timeout = 1; 19162306a36Sopenharmony_ci dst->timeout = mnl_attr_get_u32(attr); 19262306a36Sopenharmony_ci } else if (type == HANDSHAKE_A_ACCEPT_AUTH_MODE) { 19362306a36Sopenharmony_ci if (ynl_attr_validate(yarg, attr)) 19462306a36Sopenharmony_ci return MNL_CB_ERROR; 19562306a36Sopenharmony_ci dst->_present.auth_mode = 1; 19662306a36Sopenharmony_ci dst->auth_mode = mnl_attr_get_u32(attr); 19762306a36Sopenharmony_ci } else if (type == HANDSHAKE_A_ACCEPT_PEER_IDENTITY) { 19862306a36Sopenharmony_ci n_peer_identity++; 19962306a36Sopenharmony_ci } else if (type == HANDSHAKE_A_ACCEPT_CERTIFICATE) { 20062306a36Sopenharmony_ci n_certificate++; 20162306a36Sopenharmony_ci } else if (type == HANDSHAKE_A_ACCEPT_PEERNAME) { 20262306a36Sopenharmony_ci unsigned int len; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (ynl_attr_validate(yarg, attr)) 20562306a36Sopenharmony_ci return MNL_CB_ERROR; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci len = strnlen(mnl_attr_get_str(attr), mnl_attr_get_payload_len(attr)); 20862306a36Sopenharmony_ci dst->_present.peername_len = len; 20962306a36Sopenharmony_ci dst->peername = malloc(len + 1); 21062306a36Sopenharmony_ci memcpy(dst->peername, mnl_attr_get_str(attr), len); 21162306a36Sopenharmony_ci dst->peername[len] = 0; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (n_certificate) { 21662306a36Sopenharmony_ci dst->certificate = calloc(n_certificate, sizeof(*dst->certificate)); 21762306a36Sopenharmony_ci dst->n_certificate = n_certificate; 21862306a36Sopenharmony_ci i = 0; 21962306a36Sopenharmony_ci parg.rsp_policy = &handshake_x509_nest; 22062306a36Sopenharmony_ci mnl_attr_for_each(attr, nlh, sizeof(struct genlmsghdr)) { 22162306a36Sopenharmony_ci if (mnl_attr_get_type(attr) == HANDSHAKE_A_ACCEPT_CERTIFICATE) { 22262306a36Sopenharmony_ci parg.data = &dst->certificate[i]; 22362306a36Sopenharmony_ci if (handshake_x509_parse(&parg, attr)) 22462306a36Sopenharmony_ci return MNL_CB_ERROR; 22562306a36Sopenharmony_ci i++; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci if (n_peer_identity) { 23062306a36Sopenharmony_ci dst->peer_identity = calloc(n_peer_identity, sizeof(*dst->peer_identity)); 23162306a36Sopenharmony_ci dst->n_peer_identity = n_peer_identity; 23262306a36Sopenharmony_ci i = 0; 23362306a36Sopenharmony_ci mnl_attr_for_each(attr, nlh, sizeof(struct genlmsghdr)) { 23462306a36Sopenharmony_ci if (mnl_attr_get_type(attr) == HANDSHAKE_A_ACCEPT_PEER_IDENTITY) { 23562306a36Sopenharmony_ci dst->peer_identity[i] = mnl_attr_get_u32(attr); 23662306a36Sopenharmony_ci i++; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci return MNL_CB_OK; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistruct handshake_accept_rsp * 24562306a36Sopenharmony_cihandshake_accept(struct ynl_sock *ys, struct handshake_accept_req *req) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci struct ynl_req_state yrs = { .yarg = { .ys = ys, }, }; 24862306a36Sopenharmony_ci struct handshake_accept_rsp *rsp; 24962306a36Sopenharmony_ci struct nlmsghdr *nlh; 25062306a36Sopenharmony_ci int err; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci nlh = ynl_gemsg_start_req(ys, ys->family_id, HANDSHAKE_CMD_ACCEPT, 1); 25362306a36Sopenharmony_ci ys->req_policy = &handshake_accept_nest; 25462306a36Sopenharmony_ci yrs.yarg.rsp_policy = &handshake_accept_nest; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (req->_present.handler_class) 25762306a36Sopenharmony_ci mnl_attr_put_u32(nlh, HANDSHAKE_A_ACCEPT_HANDLER_CLASS, req->handler_class); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci rsp = calloc(1, sizeof(*rsp)); 26062306a36Sopenharmony_ci yrs.yarg.data = rsp; 26162306a36Sopenharmony_ci yrs.cb = handshake_accept_rsp_parse; 26262306a36Sopenharmony_ci yrs.rsp_cmd = HANDSHAKE_CMD_ACCEPT; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci err = ynl_exec(ys, nlh, &yrs); 26562306a36Sopenharmony_ci if (err < 0) 26662306a36Sopenharmony_ci goto err_free; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return rsp; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cierr_free: 27162306a36Sopenharmony_ci handshake_accept_rsp_free(rsp); 27262306a36Sopenharmony_ci return NULL; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci/* HANDSHAKE_CMD_ACCEPT - notify */ 27662306a36Sopenharmony_civoid handshake_accept_ntf_free(struct handshake_accept_ntf *rsp) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci unsigned int i; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci free(rsp->obj.peer_identity); 28162306a36Sopenharmony_ci for (i = 0; i < rsp->obj.n_certificate; i++) 28262306a36Sopenharmony_ci handshake_x509_free(&rsp->obj.certificate[i]); 28362306a36Sopenharmony_ci free(rsp->obj.certificate); 28462306a36Sopenharmony_ci free(rsp->obj.peername); 28562306a36Sopenharmony_ci free(rsp); 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci/* ============== HANDSHAKE_CMD_DONE ============== */ 28962306a36Sopenharmony_ci/* HANDSHAKE_CMD_DONE - do */ 29062306a36Sopenharmony_civoid handshake_done_req_free(struct handshake_done_req *req) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci free(req->remote_auth); 29362306a36Sopenharmony_ci free(req); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ciint handshake_done(struct ynl_sock *ys, struct handshake_done_req *req) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct nlmsghdr *nlh; 29962306a36Sopenharmony_ci int err; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci nlh = ynl_gemsg_start_req(ys, ys->family_id, HANDSHAKE_CMD_DONE, 1); 30262306a36Sopenharmony_ci ys->req_policy = &handshake_done_nest; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (req->_present.status) 30562306a36Sopenharmony_ci mnl_attr_put_u32(nlh, HANDSHAKE_A_DONE_STATUS, req->status); 30662306a36Sopenharmony_ci if (req->_present.sockfd) 30762306a36Sopenharmony_ci mnl_attr_put_u32(nlh, HANDSHAKE_A_DONE_SOCKFD, req->sockfd); 30862306a36Sopenharmony_ci for (unsigned int i = 0; i < req->n_remote_auth; i++) 30962306a36Sopenharmony_ci mnl_attr_put_u32(nlh, HANDSHAKE_A_DONE_REMOTE_AUTH, req->remote_auth[i]); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci err = ynl_exec(ys, nlh, NULL); 31262306a36Sopenharmony_ci if (err < 0) 31362306a36Sopenharmony_ci return -1; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic const struct ynl_ntf_info handshake_ntf_info[] = { 31962306a36Sopenharmony_ci [HANDSHAKE_CMD_READY] = { 32062306a36Sopenharmony_ci .alloc_sz = sizeof(struct handshake_accept_ntf), 32162306a36Sopenharmony_ci .cb = handshake_accept_rsp_parse, 32262306a36Sopenharmony_ci .policy = &handshake_accept_nest, 32362306a36Sopenharmony_ci .free = (void *)handshake_accept_ntf_free, 32462306a36Sopenharmony_ci }, 32562306a36Sopenharmony_ci}; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ciconst struct ynl_family ynl_handshake_family = { 32862306a36Sopenharmony_ci .name = "handshake", 32962306a36Sopenharmony_ci .ntf_info = handshake_ntf_info, 33062306a36Sopenharmony_ci .ntf_info_size = MNL_ARRAY_SIZE(handshake_ntf_info), 33162306a36Sopenharmony_ci}; 332