Exemple #1
0
/* FIXME make the output stream settable, default to stderr */
void sxc_set_debug(sxc_client_t *sx, int enabled) {
    if (!sx)
        return;
    if(enabled) {
        sxi_log_enable_level(&sx->log, SX_LOG_DEBUG);
	SXDEBUG("Debug mode is now enabled");
    } else {
        if (sxi_log_is_debug(&sx->log))
	    SXDEBUG("Debug mode is now disabled");
        sxc_set_verbose(sx, sx->verbose);
    }
}
Exemple #2
0
int sxc_filter_loadall(sxc_client_t *sx, const char *filter_dir)
{
	int ret;
	struct filter_ctx *fctx;

    SXDEBUG("Searching for filters in %s", filter_dir);
    ret = filter_loadall(sx, filter_dir);
    fctx = sxi_get_fctx(sx);
    if(!ret && fctx->filter_cnt >= 1)
	SXDEBUG("Loaded %d filter(s) from %s", fctx->filter_cnt, filter_dir);
    return ret;
}
Exemple #3
0
void sxi_filter_unloadall(sxc_client_t *sx)
{
    struct filter_ctx *fctx;
    int i;
    struct filter_handle *ph;
    struct filter_cfg *c;

    if(!sx)
	return;
    fctx = sxi_get_fctx(sx);

    if(fctx->filter_cnt < 1)
	return;

    SXDEBUG("Shutting down %d filter(s)", fctx->filter_cnt);
    for(i = 0; i < fctx->filter_cnt; i++) {
	ph = &fctx->filters[i];
	if(ph->active && ph->f->shutdown)
	    ph->f->shutdown(ph, ph->ctx);
	while(ph->cfg) {
	    c = ph->cfg;
	    free(c->volname);
	    free(c->cfg);
	    ph->cfg = ph->cfg->next;
	    free(c);
	}
	lt_dlclose((lt_dlhandle) ph->dlh);
    }
    free(fctx->filters);
}
Exemple #4
0
int sxi_hashop_end(sxi_hashop_t *hashop)
{
    int rc = 0;
    if (!hashop || !hashop->conns)
	return -1;
    rc = sxi_hashop_batch_flush(hashop);
    while (hashop->finished != hashop->queries && !sxi_curlev_poll(sxi_conns_get_curlev(hashop->conns))) {
/*        syslog(LOG_INFO,"finished: %d, queries: %d", hashop->finished,
 *        hashop->queries);*/
    }
    if (hashop->finished > hashop->ok + hashop->enoent) {
	/* TODO: overwrite error msg or not? */
	sxi_seterr(sxi_conns_get_client(hashop->conns), SXE_ECOMM, "%d failed HEAD requests",
		   hashop->finished - hashop->ok - hashop->enoent);
	return -1;
    } else if (hashop->finished != hashop->queries) {
	sxi_seterr(sxi_conns_get_client(hashop->conns), SXE_ECOMM, "%d unfinished HEAD requests",
		   hashop->queries - hashop->finished);
	return -1;
    }
    if (hashop->cb_fail) {
	sxi_seterr(sxi_conns_get_client(hashop->conns), SXE_ECOMM, "%d callback failures",
		   hashop->cb_fail);
	return -1;
    }
    if (hashop->enoent) {
        sxc_client_t *sx = sxi_conns_get_client(hashop->conns);
        SXDEBUG("enoent: %d", hashop->enoent);
	return ENOENT;
    }
    if (!rc)
        sxc_clearerr(sxi_conns_get_client(hashop->conns));
    return rc;
}
Exemple #5
0
static void batch_finish(curlev_context_t *ctx, const char *url)
{
    struct hashop_ctx *yactx = sxi_cbdata_get_hashop_ctx(ctx);
    long status = 0;
    int rc = sxi_cbdata_result(ctx, NULL, NULL, &status);
    if (!yactx) {
        WARN("NULL context");
        return;
    }
    sxi_query_free(yactx->query);

    sxi_hashop_t *hashop = yactx->hashop;
    if (!hashop) {
        WARN("NULL hashop");
        return;
    }

    char *q;
    q = yactx->hexhashes;
    if (!q) {
        WARN("NULL hexhashes");
        return;
    }
    sxc_client_t *sx = sxi_conns_get_client(sxi_cbdata_get_conns(ctx));
    jparse_t *J = yactx->J;

    SXDEBUG("batch_finish for %s (%p) : %ld", yactx->hexhashes, yactx->hexhashes, status);
    if (rc != -1 && status == 200) {
        /* some hashes (maybe all) are present,
         * the server reports us the presence ones */
        if (!J) {
            sxi_cbdata_seterr(ctx, SXE_EARG, "null JSON parser");
            hashop->cb_fail++;
        } else if(sxi_jparse_done(J)) {
            sxi_cbdata_seterr(ctx, SXE_ECOMM, "hashop failed: failed to parse cluster response");
            hashop->cb_fail++;
        }
    } else {
        unsigned i, n = strlen(q) / SXI_SHA1_TEXT_LEN;
        DEBUG("hexhashes missing all: %s (%p)", q, q);
        /* error: report all hashes as missing */
        if (hashop->cb) {
            for (i=0;i<n;i++) {
                int mapped_idx = yactx->idxs[i];
                if (mapped_idx < 0 ) {
                    WARN("uninitialized mapped_idx");
                    continue;
                }
                DEBUG("Hash index %d (%d) status: missing", mapped_idx, i);
                hashop->cb(q + i * SXI_SHA1_TEXT_LEN, mapped_idx, 404, hashop->context);
            }
        }
    }

    sxi_jparse_destroy(J);
    free(yactx->hexhashes);
    free(yactx);
}
Exemple #6
0
static int cert_from_sessioninfo(sxc_client_t *sx, const struct curl_tlssessioninfo *info, CERTCertificate **cert)
{
    if (info->backend != CURLSSLBACKEND_NSS) {
        curl_version_info_data *data = curl_version_info(CURLVERSION_NOW);
        sxi_seterr(sx, SXE_ECURL, "SSL backend mismatch: NSS expected, got %d (curl %s)",
                   info->backend, data->ssl_version ? data->ssl_version : "N/A");
        return -1;
    }
    PRFileDesc *desc = info->internals;
    if(!desc) {
        SXDEBUG("NULL PRFileDesc context");
        return -EAGAIN;
    }
    *cert = SSL_PeerCertificate(desc);
    if (!*cert) {
        PRInt32 err = PR_GetError();
        SXDEBUG("Unable to retrieve certificate for cluster: %s",
                PR_ErrorToString(err, PR_LANGUAGE_I_DEFAULT));
        return -EAGAIN;
    }
    return 0;
}
Exemple #7
0
void sxi_setclusterr(sxc_client_t *sx, const char *nodeid, const char *reqid, int status,
                     const char *msg, const char *details)
{
    char httpcode[16];
    if (!sx)
        return;
    if (!*msg) {
        snprintf(httpcode, sizeof(httpcode), "HTTP code %d", status);
        msg = httpcode;
    }
    sxi_fmt_start(&sx->log.fmt);
    sxi_fmt_msg(&sx->log.fmt, "Failed to %s: %s (", sx->op ? sx->op : "query cluster", msg);
    if (sx->op_host) {
        sxi_fmt_msg(&sx->log.fmt, "sx://%s", sx->op_host);
        if (sx->op_vol) {
            sxi_fmt_msg(&sx->log.fmt, "/%s", sx->op_vol);
            if (sx->op_path) {
                sxi_fmt_msg(&sx->log.fmt, "/%s", sx->op_path);
            }
        }
    }
    sxi_fmt_msg(&sx->log.fmt," on");
    if (nodeid)
        sxi_fmt_msg(&sx->log.fmt, " node:%s", nodeid);
    if (reqid)
        sxi_fmt_msg(&sx->log.fmt, " reqid:%s", reqid);
    sxi_fmt_msg(&sx->log.fmt, ")");
    if (status < 400 || status >= 500) {
        /* do not print details on 40x */
        if (sx->verbose && details && *details) {
            sxi_fmt_msg(&sx->log.fmt, "\nHTTP %d: %s", status, details);
        }
    }
    sxi_seterr(sx, SXE_ECOMM, "%s", sx->log.fmt.buf);
    sxi_clear_operation(sx);
    SXDEBUG("Cluster query failed (HTTP %d): %s", status, sx->errbuf);
    if (details && *details)
        SXDEBUG("Cluster error: %s", details);
}
Exemple #8
0
int sxi_hashop_batch_add(sxi_hashop_t *hashop, const char *host, unsigned idx, const unsigned char *binhash, unsigned int blocksize)
{
    unsigned hidx;
    int rc = 0;
    sxc_client_t *sx;
    if (!hashop)
        return -1;
    if (hashop->kind == HASHOP_SKIP)
        return 0;
    sx = sxi_conns_get_client(hashop->conns);
    if (!host || !binhash) {
        sxi_seterr(sx, SXE_EARG, "Null arg to hashop_batch_add");
        return -1;
    }
    if ((hashop->current_host && strcmp(host, hashop->current_host)) || blocksize != hashop->current_blocksize || hashop->hashes_count >= DOWNLOAD_MAX_BLOCKS) {
        rc = sxi_hashop_batch_flush(hashop);
    }
    hashop->current_host = host;
    hashop->current_blocksize = blocksize;
    hidx = hashop->hashes_count * SXI_SHA1_TEXT_LEN;
    if (hashop->hashes_pos + SXI_SHA1_TEXT_LEN + 1 >= sizeof(hashop->hashes) ||
        hidx + SXI_SHA1_TEXT_LEN >= sizeof(hashop->hexhashes)) {
        sxi_seterr(sx, SXE_EARG, "Out of bounds hashes_pos: %u (limit %lu), hidx: %u (limit %lu)",
                   hashop->hashes_pos, (long)sizeof(hashop->hashes),
                   hidx, (long)sizeof(hashop->hexhashes));
        return -1;
    }
    sxi_bin2hex(binhash, SXI_SHA1_BIN_LEN, hashop->hashes + hashop->hashes_pos);
    memcpy(hashop->hexhashes + hidx, hashop->hashes + hashop->hashes_pos, SXI_SHA1_TEXT_LEN);
    SXDEBUG("hash @%d (%d): %.*s", idx, hashop->hashes_count, SXI_SHA1_TEXT_LEN, hashop->hashes + hashop->hashes_pos);
    hashop->hashes_pos += SXI_SHA1_TEXT_LEN;
    hashop->hashes[hashop->hashes_pos++] = ',';
    hashop->idxs_tmp[hashop->hashes_count] = idx;
    hashop->hashes_count++;
    SXDEBUG("returning: %d", rc);
    return rc;
}
Exemple #9
0
struct filter_handle *sxi_filter_gethandle(sxc_client_t *sx, const uint8_t *uuid)
{
    struct filter_ctx *fctx;
    int i;

    fctx = sxi_get_fctx(sx);
    if(!fctx || fctx->filter_cnt <= 0) {
	SXDEBUG("No filters available");
	sxi_seterr(sx, SXE_EFILTER, "No filters available");
	return NULL;
    }

    for(i = 0; i < fctx->filter_cnt; i++)
	if(!memcmp(fctx->filters[i].uuid_bin, uuid, 16))
	    return &fctx->filters[i];

    return NULL;
}
Exemple #10
0
static int filter_register(sxc_client_t *sx, const char *filename)
{
	lt_dlhandle dlh;
	sxc_filter_t *filter;
	struct filter_ctx *fctx;
	struct filter_handle *ph = NULL;
	int i, ret = 0;

    if(strstr(filename, "libsxf_")) {
	dlh = lt_dlopen(filename);
	if(dlh) {
	    if(!(filter = (sxc_filter_t *) lt_dlsym(dlh, "sxc_filter"))) {
		SXDEBUG("Invalid filter %s: %s", filename, lt_dlerror());
		return 1;
	    }
	    if(filter->abi_version != SXF_ABI_VERSION) {
		SXDEBUG("ABI version mismatch (filter: %d, library: %d) with %s", filter->abi_version, SXF_ABI_VERSION, filename);
		lt_dlclose(dlh);
		return 1;
	    }
	    if(!filter->shortname || !filter->shortdesc || !filter->summary || !filter->uuid) {
		SXDEBUG("Invalid filter %s (name/summary/uuid fields missing)", filename);
		lt_dlclose(dlh);
		return 1;
	    }
	    SXDEBUG("Loading filter \"%s\", version %d.%d, type %d, uuid %s (%s)", filter->shortname, filter->version[0], filter->version[1], filter->type, filter->uuid, filename);
	    fctx = sxi_get_fctx(sx);
	    for(i = 0; i < fctx->filter_cnt; i++) {
		if(!strcmp(fctx->filters[i].f->uuid, filter->uuid)) {
		    if(fctx->filters[i].f->version[0] >= filter->version[0] && fctx->filters[i].f->version[1] >= filter->version[1]) {
			SXDEBUG("Skipping duplicate/older version of filter \"%s\"", filter->shortname);
			lt_dlclose(dlh);
			return 2;
		    }
		    SXDEBUG("Replacing older version (%d.%d) of filter \"%s\"", fctx->filters[i].f->version[0], fctx->filters[i].f->version[1], fctx->filters[i].f->shortname);
		    ph = &fctx->filters[i];
		    if(ph->active && ph->f->shutdown)
			ph->f->shutdown(ph, ph->ctx);
		    lt_dlclose(ph->dlh);
		    ph->active = 0;
		    ret = 2;
		}
	    }

	    if(!ph) {
		fctx->filter_cnt++;
		fctx->filters = sxi_realloc(sx, fctx->filters, fctx->filter_cnt * sizeof(struct filter_handle));

		if(!fctx->filters) {
		    fctx->filter_cnt = 0;
		    lt_dlclose(dlh);
		    return 1;
		}
		ph = &fctx->filters[fctx->filter_cnt - 1];
	    }
	    ph->dlh = dlh;
	    ph->ctx = NULL;
	    ph->active = 0;
	    ph->cfg = NULL;
            ph->sx = sx;
	    filter->tname = filter_gettname(filter->type);
	    if(sxi_uuid_parse(filter->uuid, ph->uuid_bin) == -1) {
		SXDEBUG("Invalid UUID for filter \"%s\"", filter->shortname);
		fctx->filter_cnt--;
		lt_dlclose(dlh);
		return 1;
	    }
	    ph->f = filter;
	    if(ph->f->init && ph->f->init(ph, &ph->ctx) < 0) {
		SXDEBUG("Can't initialize filter \"%s\"", filter->shortname);
		fctx->filter_cnt--;
		lt_dlclose(dlh);
		return 1;
	    }
	    ph->active = 1;
	} else {
	    SXDEBUG("Error while registering filter %s: %s", filename, lt_dlerror());
	    return 1;
	}
    }
    return ret;
}
Exemple #11
0
static int filter_loadall(sxc_client_t *sx, const char *filter_dir)
{
    struct filter_ctx *fctx;
    DIR *dir;
    struct dirent *dent;
    struct stat sb;
    char *path;
    int ret = 0, pcnt = 0;

    if(!sx || !filter_dir)
	return 1;
    fctx = sxi_get_fctx(sx);

    if(fctx->filter_cnt == -1) {
	SXDEBUG("Filter subsystem not available");
	sxi_seterr(sx, SXE_EFILTER, "Filter subsystem not available");
	return 1;
    }

    if(!(dir = opendir(filter_dir))) {
	SXDEBUG("Can't open filter directory %s", filter_dir);
	sxi_seterr(sx, SXE_EFILTER, "Can't open filter directory %s", filter_dir);
	return 1;
    }

    while((dent = readdir(dir))) {
	if(strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..")) {
		unsigned int pathlen = strlen(filter_dir) + strlen(dent->d_name) + 2;
	    path = malloc(pathlen);
	    if(!path) {
		SXDEBUG("OOM allocating path");
		sxi_setsyserr(sx, SXE_EMEM, "OOM allocating path");
		closedir(dir);
		return 1;
	    }
	    snprintf(path, pathlen, "%s/%s", filter_dir, dent->d_name); /* FIXME: path separator */
	    if(lstat(path, &sb) == -1) {
		free(path);
		continue;
	    }

	    if(S_ISDIR(sb.st_mode)) {
		ret = filter_loadall(sx, path);
	    } else if(S_ISREG(sb.st_mode) && !strncmp(dent->d_name, "libsxf_", 7) && strstr(dent->d_name, ".so")) {
		ret = filter_register(sx, path);
		if(!ret)
		    pcnt++;
		else if(ret == 2)
		    ret = 0;
	    }
	    free(path);
	}
    }
    closedir(dir);

    if(!pcnt && ret) {
	sxi_seterr(sx, SXE_EFILTER, "No filters loaded due to version mismatch");
	return ret;
    }

    /*
    if(pcnt)
	SXDEBUG("Loaded %d filter(s) from %s", pcnt, filter_dir);
    */

    return 0;
}
Exemple #12
0
static int sxi_hashop_batch(sxi_hashop_t *hashop)
{
    const char *host, *hexhashes;
    unsigned int blocksize, i, n;
    sxi_query_t *query;
    int rc;
    if (!hashop || !hashop->conns)
	return -1;
    host = hashop->current_host;
    hexhashes = hashop->hexhashes;
    if (!host || !hexhashes)
        return -1;
    blocksize = hashop->current_blocksize;
    hashop->hashes[hashop->hashes_pos] = '\0';
    hashop->hexhashes[hashop->hashes_count*SXI_SHA1_TEXT_LEN] = '\0';
    sxc_client_t *sx;

    sx = sxi_conns_get_client(hashop->conns);
    if (hashop->finished > hashop->ok + hashop->enoent) {
	sxi_seterr(sx, SXE_ECOMM, "%d failed HEAD requests",
		   hashop->finished - hashop->ok - hashop->enoent);
	return -1;
    }
    struct hashop_ctx *pp = calloc(1, sizeof(*pp));
    if (!pp) {
        sxi_seterr(sx, SXE_EMEM, "failed to allocate presence parser");
        return -1;
    }
    memcpy(pp->idxs, hashop->idxs_tmp, sizeof(hashop->idxs_tmp));
    curlev_context_t *cbdata = sxi_cbdata_create_hashop(hashop->conns, batch_finish, pp);
    if (!cbdata) {
	sxi_seterr(sx, SXE_EMEM, "failed to allocate callback");
        free(pp);
	return -1;
    }
    pp->hexhashes = strdup(hexhashes);
    if (!pp->hexhashes) {
	sxi_seterr(sx, SXE_EMEM, "failed to allocate hashbatch");
        free(pp);
	free(cbdata);
	return -1;
    }
    pp->hashop = hashop;
    SXDEBUG("a->queries: %d", hashop->queries);
    hashop->queries += strlen(hexhashes) / 40;

    SXDEBUG("hashop %d, on %s, hexhashes: %s (%p)", hashop->hashes_count, hashop->hashes, hashop->hexhashes,
            hashop->hexhashes);
    n = strlen(hexhashes) / SXI_SHA1_TEXT_LEN;
    switch (hashop->kind) {
        case HASHOP_CHECK:
            query = sxi_hashop_proto_check(sxi_conns_get_client(hashop->conns), blocksize, hashop->hashes, hashop->hashes_pos);
            break;
        case HASHOP_RESERVE:
            query = sxi_hashop_proto_reserve(sxi_conns_get_client(hashop->conns), blocksize, hashop->hashes, hashop->hashes_pos, &hashop->global_vol_id, &hashop->reserve_id, &hashop->revision_id, hashop->replica, hashop->op_expires_at);
            break;
        case HASHOP_INUSE:
            query = sxi_hashop_proto_inuse_begin(sxi_conns_get_client(hashop->conns), hashop->has_reserve_id ? &hashop->reserve_id : NULL);
            for (i=0;i < n;i++) {
                block_meta_entry_t entry;
                block_meta_t meta;
                if (hex2bin(hexhashes + i * SXI_SHA1_TEXT_LEN, SXI_SHA1_TEXT_LEN, meta.hash.b, sizeof(meta.hash.b))) {
                    sxi_query_free(query);
                    query = NULL;
                    break;
                }
                memcpy(&entry.revision_id, &hashop->revision_id, sizeof(entry.revision_id));
                memcpy(&entry.global_vol_id, &hashop->global_vol_id, sizeof(entry.global_vol_id));
                entry.replica = hashop->replica;
                meta.entries = &entry;
                meta.count = 1;
                meta.blocksize = hashop->current_blocksize;
                query = sxi_hashop_proto_inuse_hash(sx, query, &meta);
            }
            query = sxi_hashop_proto_inuse_end(sx, query);
            break;
        default:
            query = NULL;
            break;
    }
    pp->query = query;
    if (query) {
        DEBUG("Sending query to %s", query->path);
        rc = sxi_cluster_query_ev(cbdata, hashop->conns, host, query->verb, query->path, query->content, query->content_len, presence_setup_cb, presence_cb);
    } else {
        free(pp->hexhashes);
        sxi_query_free(pp->query);
        free(pp);
        rc = -1;
    }
    sxi_cbdata_unref(&cbdata);
    return rc;
}
Exemple #13
0
CURLcode sxi_verifyhost(sxc_client_t *sx, const char *hostname, X509 *server_cert)
{
    int matched = -1; /* -1 is no alternative match yet, 1 means match and 0
                         means mismatch */
    STACK_OF(GENERAL_NAME) *altnames;
    CURLcode res = CURLE_OK;

    /* get a "list" of alternative names */
    altnames = X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL);

    if(altnames) {
        int numalts;
        int i;

        /* get amount of alternatives, RFC2459 claims there MUST be at least
           one, but we don't depend on it... */
        numalts = sk_GENERAL_NAME_num(altnames);

        /* loop through all alternatives while none has matched */
        for(i=0; (i<numalts) && (matched != 1); i++) {
            /* get a handle to alternative name number i */
            const GENERAL_NAME *check = sk_GENERAL_NAME_value(altnames, i);

            /* only check alternatives of the same type the target is */
            if(check->type == GEN_DNS) {
                /* get data and length */
                const char *altptr = (char *)ASN1_STRING_data(check->d.ia5);
                size_t altlen = (size_t) ASN1_STRING_length(check->d.ia5);

                /* name/pattern comparison */
                /* The OpenSSL man page explicitly says: "In general it cannot be
                   assumed that the data returned by ASN1_STRING_data() is null
                   terminated or does not contain embedded nulls." But also that
                   "The actual format of the data will depend on the actual string
                   type itself: for example for and IA5String the data will be ASCII"

                   Curl uses strlen(altptr) == altlen here to check for embedded \0s.
                   We use memchr() to be safer from a possible non-null terminated string.
                   */
                if(!memchr(altptr, 0, altlen) &&
                   /* if this isn't true, there was an embedded zero in the name
                      string and we cannot match it. */
                   Curl_cert_hostcheck(altptr, hostname))
                    matched = 1;
                else
                    matched = 0;
            }
        }
        GENERAL_NAMES_free(altnames);
    }

    if(matched == 1)
        /* an alternative name matched the server hostname */
        SXDEBUG("\t subjectAltName: %s matched\n", hostname);
    else if(matched == 0) {
        /* an alternative name field existed, but didn't match and then
           we MUST fail */
        sxi_seterr(sx, SXE_ECOMM, "subjectAltName does not match %s\n", hostname);
        res = CURLE_PEER_FAILED_VERIFICATION;
    }
    else {
        /* we have to look to the last occurrence of a commonName in the
           distinguished one to get the most significant one. */
        int j,i=-1 ;

        /* The following is done because of a bug in 0.9.6b */

        unsigned char *nulstr = (unsigned char *)"";
        unsigned char *peer_CN = nulstr;

        X509_NAME *name = X509_get_subject_name(server_cert) ;
        if(name)
            while((j = X509_NAME_get_index_by_NID(name, NID_commonName, i))>=0)
                i=j;

        /* we have the name entry and we will now convert this to a string
           that we can use for comparison. Doing this we support BMPstring,
           UTF8 etc. */

        if(i>=0) {
            ASN1_STRING *tmp = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name,i));

            /* In OpenSSL 0.9.7d and earlier, ASN1_STRING_to_UTF8 fails if the input
               is already UTF-8 encoded. We check for this case and copy the raw
               string manually to avoid the problem. This code can be made
               conditional in the future when OpenSSL has been fixed. Work-around
               brought by Alexis S. L. Carvalho. */
            if(tmp) {
                if(ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) {
                    j = ASN1_STRING_length(tmp);
                    if(j >= 0) {
                        peer_CN = OPENSSL_malloc(j+1);
                        if(peer_CN) {
                            memcpy(peer_CN, ASN1_STRING_data(tmp), j);
                            peer_CN[j] = '\0';
                        }
                    }
                }
                else /* not a UTF8 name */
                    j = ASN1_STRING_to_UTF8(&peer_CN, tmp);

                if(peer_CN && memchr(peer_CN, 0, j)) {
                    /* there was a terminating zero before the end of string, this
                       cannot match and we return failure! */
                    sxi_seterr(sx, SXE_ECOMM, "SSL: illegal cert name field");
                    res = CURLE_PEER_FAILED_VERIFICATION;
                }
            }
        }

        if(peer_CN == nulstr)
            peer_CN = NULL;
        /* curl would convert from UTF8 to host encoding if built with HAVE_ICONV (not default) */

        if(res)
            /* error already detected, pass through */
            ;
        else if(!peer_CN) {
            SXDEBUG("SSL: unable to obtain common name from peer certificate");
            res = CURLE_PEER_FAILED_VERIFICATION;
        }
        else if(!Curl_cert_hostcheck((const char *)peer_CN, hostname)) {
            sxi_seterr(sx, SXE_ECOMM, "SSL: certificate subject name '%s' does not match "
                    "target host name '%s'", peer_CN, hostname);
            res = CURLE_PEER_FAILED_VERIFICATION;
        }
        else {
            SXDEBUG("\t common name: %s (matched)\n", peer_CN);
        }
        if(peer_CN)
            OPENSSL_free(peer_CN);
    }
    return res;
}