static int add_custom_ext_intern(SSL_CTX *ctx, ENDPOINT role,
                                 unsigned int ext_type,
                                 unsigned int context,
                                 SSL_custom_ext_add_cb_ex add_cb,
                                 SSL_custom_ext_free_cb_ex free_cb,
                                 void *add_arg,
                                 SSL_custom_ext_parse_cb_ex parse_cb,
                                 void *parse_arg)
{
    custom_ext_methods *exts = &ctx->cert->custext;
    custom_ext_method *meth, *tmp;
    if (add_cb == NULL && free_cb != NULL)
        return 0;
#ifndef OPENSSL_NO_CT
    if (ext_type == TLSEXT_TYPE_signed_certificate_timestamp
            && (context & SSL_EXT_CLIENT_HELLO) != 0
            && SSL_CTX_ct_is_enabled(ctx))
        return 0;
#endif
    if (SSL_extension_supported(ext_type)
            && ext_type != TLSEXT_TYPE_signed_certificate_timestamp)
        return 0;
    if (ext_type > 0xffff)
        return 0;
    if (custom_ext_find(exts, role, ext_type, NULL))
        return 0;
    tmp = OPENSSL_realloc(exts->meths,
                          (exts->meths_count + 1) * sizeof(custom_ext_method));
    if (tmp == NULL)
        return 0;
    exts->meths = tmp;
    meth = exts->meths + exts->meths_count;
    memset(meth, 0, sizeof(*meth));
    meth->role = role;
    meth->context = context;
    meth->parse_cb = parse_cb;
    meth->add_cb = add_cb;
    meth->free_cb = free_cb;
    meth->ext_type = ext_type;
    meth->add_arg = add_arg;
    meth->parse_arg = parse_arg;
    exts->meths_count++;
    return 1;
}
['static int test_custom_exts(int tst)\n{\n    SSL_CTX *cctx = NULL, *sctx = NULL, *sctx2 = NULL;\n    SSL *clientssl = NULL, *serverssl = NULL;\n    int testresult = 0;\n    static int server = 1;\n    static int client = 0;\n    SSL_SESSION *sess = NULL;\n    unsigned int context;\n#if defined(OPENSSL_NO_TLS1_2) && !defined(OPENSSL_NO_TLS1_3)\n    if (tst < 3)\n        return 1;\n#endif\n    clntaddoldcb = clntparseoldcb = srvaddoldcb = srvparseoldcb = 0;\n    clntaddnewcb = clntparsenewcb = srvaddnewcb = srvparsenewcb = 0;\n    snicb = 0;\n    if (!TEST_true(create_ssl_ctx_pair(TLS_server_method(), TLS_client_method(),\n                                       TLS1_VERSION, 0,\n                                       &sctx, &cctx, cert, privkey)))\n        goto end;\n    if (tst == 2\n            && !TEST_true(create_ssl_ctx_pair(TLS_server_method(), NULL,\n                                              TLS1_VERSION, 0,\n                                              &sctx2, NULL, cert, privkey)))\n        goto end;\n    if (tst < 3) {\n        SSL_CTX_set_options(cctx, SSL_OP_NO_TLSv1_3);\n        SSL_CTX_set_options(sctx, SSL_OP_NO_TLSv1_3);\n        if (sctx2 != NULL)\n            SSL_CTX_set_options(sctx2, SSL_OP_NO_TLSv1_3);\n    }\n    if (tst == 4) {\n        context = SSL_EXT_CLIENT_HELLO\n                  | SSL_EXT_TLS1_2_SERVER_HELLO\n                  | SSL_EXT_TLS1_3_SERVER_HELLO\n                  | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS\n                  | SSL_EXT_TLS1_3_CERTIFICATE\n                  | SSL_EXT_TLS1_3_NEW_SESSION_TICKET;\n    } else {\n        context = SSL_EXT_CLIENT_HELLO\n                  | SSL_EXT_TLS1_2_SERVER_HELLO\n                  | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS;\n    }\n    if (tst == 0) {\n        if (!TEST_true(SSL_CTX_add_client_custom_ext(cctx, TEST_EXT_TYPE1,\n                                                     old_add_cb, old_free_cb,\n                                                     &client, old_parse_cb,\n                                                     &client)))\n            goto end;\n    } else {\n        if (!TEST_true(SSL_CTX_add_custom_ext(cctx, TEST_EXT_TYPE1, context,\n                                              new_add_cb, new_free_cb,\n                                              &client, new_parse_cb, &client)))\n            goto end;\n    }\n    if (!TEST_false(SSL_CTX_add_client_custom_ext(cctx, TEST_EXT_TYPE1,\n                                                  old_add_cb, old_free_cb,\n                                                  &client, old_parse_cb,\n                                                  &client))\n            || !TEST_false(SSL_CTX_add_custom_ext(cctx, TEST_EXT_TYPE1,\n                                                  context, new_add_cb,\n                                                  new_free_cb, &client,\n                                                  new_parse_cb, &client)))\n        goto end;\n    if (tst == 0) {\n        if (!TEST_true(SSL_CTX_add_server_custom_ext(sctx, TEST_EXT_TYPE1,\n                                                     old_add_cb, old_free_cb,\n                                                     &server, old_parse_cb,\n                                                     &server)))\n            goto end;\n    } else {\n        if (!TEST_true(SSL_CTX_add_custom_ext(sctx, TEST_EXT_TYPE1, context,\n                                              new_add_cb, new_free_cb,\n                                              &server, new_parse_cb, &server)))\n            goto end;\n        if (sctx2 != NULL\n                && !TEST_true(SSL_CTX_add_custom_ext(sctx2, TEST_EXT_TYPE1,\n                                                     context, new_add_cb,\n                                                     new_free_cb, &server,\n                                                     new_parse_cb, &server)))\n            goto end;\n    }\n    if (!TEST_false(SSL_CTX_add_server_custom_ext(sctx, TEST_EXT_TYPE1,\n                                                  old_add_cb, old_free_cb,\n                                                  &server, old_parse_cb,\n                                                  &server))\n            || !TEST_false(SSL_CTX_add_custom_ext(sctx, TEST_EXT_TYPE1,\n                                                  context, new_add_cb,\n                                                  new_free_cb, &server,\n                                                  new_parse_cb, &server)))\n        goto end;\n    if (tst == 2) {\n        if (!TEST_true(SSL_CTX_set_tlsext_servername_callback(sctx, sni_cb))\n                || !TEST_true(SSL_CTX_set_tlsext_servername_arg(sctx, sctx2)))\n            goto end;\n    }\n    if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl,\n                                      &clientssl, NULL, NULL))\n            || !TEST_true(create_ssl_connection(serverssl, clientssl,\n                                                SSL_ERROR_NONE)))\n        goto end;\n    if (tst == 0) {\n        if (clntaddoldcb != 1\n                || clntparseoldcb != 1\n                || srvaddoldcb != 1\n                || srvparseoldcb != 1)\n            goto end;\n    } else if (tst == 1 || tst == 2 || tst == 3) {\n        if (clntaddnewcb != 1\n                || clntparsenewcb != 1\n                || srvaddnewcb != 1\n                || srvparsenewcb != 1\n                || (tst != 2 && snicb != 0)\n                || (tst == 2 && snicb != 1))\n            goto end;\n    } else {\n        if (clntaddnewcb != 1\n                || clntparsenewcb != 5\n                || srvaddnewcb != 5\n                || srvparsenewcb != 1)\n            goto end;\n    }\n    sess = SSL_get1_session(clientssl);\n    SSL_shutdown(clientssl);\n    SSL_shutdown(serverssl);\n    SSL_free(serverssl);\n    SSL_free(clientssl);\n    serverssl = clientssl = NULL;\n    if (tst == 3) {\n        testresult = 1;\n        goto end;\n    }\n    if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl,\n                                      NULL, NULL))\n            || !TEST_true(SSL_set_session(clientssl, sess))\n            || !TEST_true(create_ssl_connection(serverssl, clientssl,\n                                               SSL_ERROR_NONE)))\n        goto end;\n    if (tst == 0) {\n        if (clntaddoldcb != 2\n                || clntparseoldcb != 1\n                || srvaddoldcb != 1\n                || srvparseoldcb != 1)\n            goto end;\n    } else if (tst == 1 || tst == 2 || tst == 3) {\n        if (clntaddnewcb != 2\n                || clntparsenewcb != 2\n                || srvaddnewcb != 2\n                || srvparsenewcb != 2)\n            goto end;\n    } else {\n        if (clntaddnewcb != 2\n                || clntparsenewcb != 8\n                || srvaddnewcb != 8\n                || srvparsenewcb != 2)\n            goto end;\n    }\n    testresult = 1;\nend:\n    SSL_SESSION_free(sess);\n    SSL_free(serverssl);\n    SSL_free(clientssl);\n    SSL_CTX_free(sctx2);\n    SSL_CTX_free(sctx);\n    SSL_CTX_free(cctx);\n    return testresult;\n}', 'int SSL_CTX_add_client_custom_ext(SSL_CTX *ctx, unsigned int ext_type,\n                                  custom_ext_add_cb add_cb,\n                                  custom_ext_free_cb free_cb,\n                                  void *add_arg,\n                                  custom_ext_parse_cb parse_cb, void *parse_arg)\n{\n    return add_old_custom_ext(ctx, ENDPOINT_CLIENT, ext_type,\n                              SSL_EXT_TLS1_2_AND_BELOW_ONLY\n                              | SSL_EXT_CLIENT_HELLO\n                              | SSL_EXT_TLS1_2_SERVER_HELLO\n                              | SSL_EXT_IGNORE_ON_RESUMPTION,\n                              add_cb, free_cb, add_arg, parse_cb, parse_arg);\n}', 'static int add_old_custom_ext(SSL_CTX *ctx, ENDPOINT role,\n                              unsigned int ext_type,\n                              unsigned int context,\n                              custom_ext_add_cb add_cb,\n                              custom_ext_free_cb free_cb,\n                              void *add_arg,\n                              custom_ext_parse_cb parse_cb, void *parse_arg)\n{\n    custom_ext_add_cb_wrap *add_cb_wrap\n        = OPENSSL_malloc(sizeof(*add_cb_wrap));\n    custom_ext_parse_cb_wrap *parse_cb_wrap\n        = OPENSSL_malloc(sizeof(*parse_cb_wrap));\n    int ret;\n    if (add_cb_wrap == NULL || parse_cb_wrap == NULL) {\n        OPENSSL_free(add_cb_wrap);\n        OPENSSL_free(parse_cb_wrap);\n        return 0;\n    }\n    add_cb_wrap->add_arg = add_arg;\n    add_cb_wrap->add_cb = add_cb;\n    add_cb_wrap->free_cb = free_cb;\n    parse_cb_wrap->parse_arg = parse_arg;\n    parse_cb_wrap->parse_cb = parse_cb;\n    ret = add_custom_ext_intern(ctx, role, ext_type,\n                                context,\n                                custom_ext_add_old_cb_wrap,\n                                custom_ext_free_old_cb_wrap,\n                                add_cb_wrap,\n                                custom_ext_parse_old_cb_wrap,\n                                parse_cb_wrap);\n    if (!ret) {\n        OPENSSL_free(add_cb_wrap);\n        OPENSSL_free(parse_cb_wrap);\n    }\n    return ret;\n}', 'int SSL_CTX_add_custom_ext(SSL_CTX *ctx, unsigned int ext_type,\n                           unsigned int context,\n                           SSL_custom_ext_add_cb_ex add_cb,\n                           SSL_custom_ext_free_cb_ex free_cb,\n                           void *add_arg,\n                           SSL_custom_ext_parse_cb_ex parse_cb, void *parse_arg)\n{\n    return add_custom_ext_intern(ctx, ENDPOINT_BOTH, ext_type, context, add_cb,\n                                 free_cb, add_arg, parse_cb, parse_arg);\n}', 'static int add_custom_ext_intern(SSL_CTX *ctx, ENDPOINT role,\n                                 unsigned int ext_type,\n                                 unsigned int context,\n                                 SSL_custom_ext_add_cb_ex add_cb,\n                                 SSL_custom_ext_free_cb_ex free_cb,\n                                 void *add_arg,\n                                 SSL_custom_ext_parse_cb_ex parse_cb,\n                                 void *parse_arg)\n{\n    custom_ext_methods *exts = &ctx->cert->custext;\n    custom_ext_method *meth, *tmp;\n    if (add_cb == NULL && free_cb != NULL)\n        return 0;\n#ifndef OPENSSL_NO_CT\n    if (ext_type == TLSEXT_TYPE_signed_certificate_timestamp\n            && (context & SSL_EXT_CLIENT_HELLO) != 0\n            && SSL_CTX_ct_is_enabled(ctx))\n        return 0;\n#endif\n    if (SSL_extension_supported(ext_type)\n            && ext_type != TLSEXT_TYPE_signed_certificate_timestamp)\n        return 0;\n    if (ext_type > 0xffff)\n        return 0;\n    if (custom_ext_find(exts, role, ext_type, NULL))\n        return 0;\n    tmp = OPENSSL_realloc(exts->meths,\n                          (exts->meths_count + 1) * sizeof(custom_ext_method));\n    if (tmp == NULL)\n        return 0;\n    exts->meths = tmp;\n    meth = exts->meths + exts->meths_count;\n    memset(meth, 0, sizeof(*meth));\n    meth->role = role;\n    meth->context = context;\n    meth->parse_cb = parse_cb;\n    meth->add_cb = add_cb;\n    meth->free_cb = free_cb;\n    meth->ext_type = ext_type;\n    meth->add_arg = add_arg;\n    meth->parse_arg = parse_arg;\n    exts->meths_count++;\n    return 1;\n}', 'custom_ext_method *custom_ext_find(const custom_ext_methods *exts,\n                                   ENDPOINT role, unsigned int ext_type,\n                                   size_t *idx)\n{\n    size_t i;\n    custom_ext_method *meth = exts->meths;\n    for (i = 0; i < exts->meths_count; i++, meth++) {\n        if (ext_type == meth->ext_type\n                && (role == ENDPOINT_BOTH || role == meth->role\n                    || meth->role == ENDPOINT_BOTH)) {\n            if (idx != NULL)\n                *idx = i;\n            return meth;\n        }\n    }\n    return NULL;\n}']