Beispiel #1
0
struct nc_server_reply *
op_lock(struct lyd_node *rpc, struct nc_session *ncs)
{
    struct np2_sessions *sessions;
    sr_datastore_t ds = 0;
    struct nc_session **dsl = NULL;
    time_t *dst;
    struct ly_set *nodeset;
    struct nc_server_error *e;
    struct nc_server_reply *ereply = NULL;
    const char *dsname;

    /* get sysrepo connections for this session */
    sessions = (struct np2_sessions *)nc_session_get_data(ncs);

    if (np2srv_sr_check_exec_permission(sessions->srs, "/ietf-netconf:lock", &ereply)) {
        goto finish;
    }

    /* get know which datastore is being affected */
    nodeset = lyd_find_path(rpc, "/ietf-netconf:lock/target/*");
    dsname = nodeset->set.d[0]->schema->name;
    ly_set_free(nodeset);

    if (!strcmp(dsname, "running")) {
        /* TODO additional requirements in case of supporting confirmed-commit */
        ds = SR_DS_RUNNING;
        dsl = &dslock.running;
        dst = &dslock.running_time;
    } else if (!strcmp(dsname, "startup")) {
        ds = SR_DS_STARTUP;
        dsl = &dslock.startup;
        dst = &dslock.startup_time;
    } else if (!strcmp(dsname, "candidate")) {
        ds = SR_DS_CANDIDATE;
        dsl = &dslock.candidate;
        dst = &dslock.candidate_time;
    } else {
        EINT;
        e = nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_PROT);
        nc_err_set_msg(e, np2log_lasterr(), "en");
        ereply = nc_server_reply_err(e);
        goto finish;
    }
    if (ds != sessions->ds) {
        /* update sysrepo session */
        if (np2srv_sr_session_switch_ds(sessions->srs, ds, &ereply)) {
            goto finish;
        }
        sessions->ds = ds;
    }

    pthread_rwlock_rdlock(&dslock_rwl);
    if (*dsl) {
lock_held:
        /* lock already held */
        pthread_rwlock_unlock(&dslock_rwl);
        ERR("Locking datastore %s by session %d failed (datastore is already locked by session %d).",
            dsname, nc_session_get_id(ncs), nc_session_get_id(*dsl));
        e = nc_err(NC_ERR_LOCK_DENIED, nc_session_get_id(*dsl));
        nc_err_set_msg(e, np2log_lasterr(), "en");
        ereply = nc_server_reply_err(e);
        goto finish;
    }
    pthread_rwlock_unlock(&dslock_rwl);

    pthread_rwlock_wrlock(&dslock_rwl);
    /* check again dsl, it could change between unlock and wrlock */
    if (*dsl) {
        goto lock_held;
    }

    if (np2srv_sr_lock_datastore(sessions->srs, &ereply)) {
        /* lock is held outside Netopeer */
        pthread_rwlock_unlock(&dslock_rwl);
        /* add lock denied error */
        ERR("Locking datastore %s by session %d failed.", dsname, nc_session_get_id(ncs));
        e = nc_err(NC_ERR_LOCK_DENIED, 0);
        nc_err_set_msg(e, np2log_lasterr(), "en");
        nc_server_reply_add_err(ereply, e);
        goto finish;
    }

    /* update local information about locks */
    *dsl = ncs;
    *dst = time(NULL);
    pthread_rwlock_unlock(&dslock_rwl);

    /* build positive RPC Reply */
    ereply = nc_server_reply_ok();

finish:
    return ereply;
}
Beispiel #2
0
struct nc_server_reply *
op_validate(struct lyd_node *rpc, struct nc_session *ncs)
{
    struct np2_sessions *sessions;
    struct ly_set *nodeset;
    struct nc_server_error *e = NULL;
    int rc;
    struct lyd_node *config = NULL;
    struct lyd_node_anyxml *axml;
    const char *dsname;
    sr_datastore_t ds = SR_DS_CANDIDATE;

    /* get sysrepo connections for this session */
    sessions = (struct np2_sessions *)nc_session_get_data(ncs);

    /* get know which datastore is being affected */
    nodeset = lyd_get_node(rpc, "/ietf-netconf:validate/source/*");
    dsname = nodeset->set.d[0]->schema->name;
    axml = (struct lyd_node_anyxml *)nodeset->set.d[0];
    ly_set_free(nodeset);
    if (!strcmp(dsname, "running")) {
        ds = SR_DS_RUNNING;
    } else if (!strcmp(dsname, "startup")) {
        ds = SR_DS_STARTUP;
    } else if (!strcmp(dsname, "candidate")) {
        ds = SR_DS_CANDIDATE;
    } else if (!strcmp(dsname, "config")) {
        /* get data tree to validate */
        config = lyd_parse_xml(rpc->schema->module->ctx, &axml->value.xml, LYD_OPT_CONFIG | LYD_OPT_DESTRUCT);
        if (ly_errno != LY_SUCCESS) {
            ly_set_free(nodeset);
            goto error;
        }
        rc = lyd_validate(&config, LYD_OPT_CONFIG, np2srv.ly_ctx);

        /* cleanup */
        lyd_free_withsiblings(config);

        goto done;
    }
    /* TODO support URL */

    if (ds != sessions->ds) {
        /* update sysrepo session */
        sr_session_switch_ds(sessions->srs, ds);
        sessions->ds = ds;
    }
    if (ds != SR_DS_CANDIDATE) {
        /* refresh datastore content */
        if (sr_session_refresh(sessions->srs) != SR_ERR_OK) {
            goto error;
        }
    }

    /* validate sysrepo's datastore */
    rc = sr_validate(sessions->srs);
    if (rc != SR_ERR_OK) {
        goto error;
    }

done:

    return nc_server_reply_ok();

error:
    /* handle error */
    if (!e) {
        e = nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP);
        nc_err_set_msg(e, np2log_lasterr(), "en");
    }

    return nc_server_reply_err(e);
}
Beispiel #3
0
struct nc_server_reply *
op_unlock(struct lyd_node *rpc, struct nc_session *ncs)
{
    struct np2_sessions *sessions;
    sr_datastore_t ds = 0;
    struct nc_session **dsl = NULL;
    time_t *dst;
    struct ly_set *nodeset;
    const char *dsname;
    struct nc_server_error *e;
    struct nc_server_reply *ereply = NULL;

    /* get sysrepo connections for this session */
    sessions = (struct np2_sessions *)nc_session_get_data(ncs);

    if (np2srv_sr_check_exec_permission(sessions->srs, "/ietf-netconf:unlock", &ereply)) {
        goto finish;
    }

    /* get know which datastore is being affected */
    nodeset = lyd_find_path(rpc, "/ietf-netconf:unlock/target/*");
    dsname = nodeset->set.d[0]->schema->name;
    ly_set_free(nodeset);

    if (!strcmp(dsname, "running")) {
        ds = SR_DS_RUNNING;
        dsl = &dslock.running;
        dst = &dslock.running_time;
    } else if (!strcmp(dsname, "startup")) {
        ds = SR_DS_STARTUP;
        dsl = &dslock.startup;
        dst = &dslock.startup_time;
    } else if (!strcmp(dsname, "candidate")) {
        ds = SR_DS_CANDIDATE;
        dsl = &dslock.candidate;
        dst = &dslock.candidate_time;
    } else {
        EINT;
        e = nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_PROT);
        nc_err_set_msg(e, np2log_lasterr(), "en");
        ereply = nc_server_reply_err(e);
        goto finish;
    }
    if (ds != sessions->ds) {
        /* update sysrepo session */
        if (np2srv_sr_session_switch_ds(sessions->srs, ds, &ereply)) {
            goto finish;
        }
        sessions->ds = ds;
    }

    pthread_rwlock_rdlock(&dslock_rwl);
    if (!(*dsl)) {
        /* lock is not held */
        pthread_rwlock_unlock(&dslock_rwl);
        ERR("Unlocking datastore %s by session %d failed (lock is not active).",
            dsname, nc_session_get_id(ncs));
        e = nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_PROT);
        nc_err_set_msg(e, np2log_lasterr(), "en");
        ereply = nc_server_reply_err(e);
        goto finish;
    } else {
        /* lock is held, but by who? */
        if ((*dsl) != ncs) {
            /* by someone else */
            pthread_rwlock_unlock(&dslock_rwl);
            ERR("Unlocking datastore %s by session %d failed (lock is held by session %d).",
                dsname, nc_session_get_id(ncs), nc_session_get_id(*dsl));
            e = nc_err(NC_ERR_LOCK_DENIED, nc_session_get_id(*dsl));
            nc_err_set_msg(e, np2log_lasterr(), "en");
            ereply = nc_server_reply_err(e);
            goto finish;
        }
    }
    pthread_rwlock_unlock(&dslock_rwl);
    pthread_rwlock_wrlock(&dslock_rwl);

    if (np2srv_sr_unlock_datastore(sessions->srs, &ereply)) {
        /* lock is held outside Netopeer */
        pthread_rwlock_unlock(&dslock_rwl);
        /* add lock denied error */
        ERR("Unlocking datastore %s by session %d failed.", dsname, nc_session_get_id(ncs));
        e = nc_err(NC_ERR_LOCK_DENIED, 0);
        nc_err_set_msg(e, np2log_lasterr(), "en");
        nc_server_reply_add_err(ereply, e);
        goto finish;
    }

    /* according to RFC 6241 8.3.5.2, discard changes */
    np2srv_sr_discard_changes(sessions->srs, NULL);

    /* update local information about locks */
    *dsl = NULL;
    *dst = 0;

    pthread_rwlock_unlock(&dslock_rwl);

    /* build positive RPC Reply */
    ereply = nc_server_reply_ok();

finish:
    return ereply;
}
Beispiel #4
0
/* return NC_MSG_ERROR can change session status, acquires IO lock as needed */
NC_MSG_TYPE
nc_read_msg_io(struct nc_session *session, int io_timeout, struct lyxml_elem **data, int passing_io_lock)
{
    int ret, io_locked = passing_io_lock;
    char *msg = NULL, *chunk;
    uint64_t chunk_len, len = 0;
    /* use timeout in milliseconds instead seconds */
    uint32_t inact_timeout = NC_READ_INACT_TIMEOUT * 1000;
    struct timespec ts_act_timeout;
    struct nc_server_reply *reply;

    assert(session && data);
    *data = NULL;

    if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) {
        ERR("Session %u: invalid session to read from.", session->id);
        ret = NC_MSG_ERROR;
        goto cleanup;
    }

    nc_gettimespec_mono(&ts_act_timeout);
    nc_addtimespec(&ts_act_timeout, NC_READ_ACT_TIMEOUT * 1000);

    if (!io_locked) {
        /* SESSION IO LOCK */
        ret = nc_session_io_lock(session, io_timeout, __func__);
        if (ret < 0) {
            ret = NC_MSG_ERROR;
            goto cleanup;
        } else if (!ret) {
            ret = NC_MSG_WOULDBLOCK;
            goto cleanup;
        }
        io_locked = 1;
    }

    /* read the message */
    switch (session->version) {
    case NC_VERSION_10:
        ret = nc_read_until(session, NC_VERSION_10_ENDTAG, 0, inact_timeout, &ts_act_timeout, &msg);
        if (ret == -1) {
            ret = NC_MSG_ERROR;
            goto cleanup;
        }

        /* cut off the end tag */
        msg[ret - NC_VERSION_10_ENDTAG_LEN] = '\0';
        break;
    case NC_VERSION_11:
        while (1) {
            ret = nc_read_until(session, "\n#", 0, inact_timeout, &ts_act_timeout, NULL);
            if (ret == -1) {
                ret = NC_MSG_ERROR;
                goto cleanup;
            }
            ret = nc_read_until(session, "\n", 0, inact_timeout, &ts_act_timeout, &chunk);
            if (ret == -1) {
                ret = NC_MSG_ERROR;
                goto cleanup;
            }

            if (!strcmp(chunk, "#\n")) {
                /* end of chunked framing message */
                free(chunk);
                if (!msg) {
                    ERR("Session %u: invalid frame chunk delimiters.", session->id);
                    goto malformed_msg;
                }
                break;
            }

            /* convert string to the size of the following chunk */
            chunk_len = strtoul(chunk, (char **)NULL, 10);
            free(chunk);
            if (!chunk_len) {
                ERR("Session %u: invalid frame chunk size detected, fatal error.", session->id);
                goto malformed_msg;
            }

            /* now we have size of next chunk, so read the chunk */
            ret = nc_read_chunk(session, chunk_len, inact_timeout, &ts_act_timeout, &chunk);
            if (ret == -1) {
                ret = NC_MSG_ERROR;
                goto cleanup;
            }

            /* realloc message buffer, remember to count terminating null byte */
            msg = nc_realloc(msg, len + chunk_len + 1);
            if (!msg) {
                ERRMEM;
                ret = NC_MSG_ERROR;
                goto cleanup;
            }
            memcpy(msg + len, chunk, chunk_len);
            len += chunk_len;
            msg[len] = '\0';
            free(chunk);
        }

        break;
    }

    /* SESSION IO UNLOCK */
    assert(io_locked);
    nc_session_io_unlock(session, __func__);
    io_locked = 0;

    DBG("Session %u: received message:\n%s\n", session->id, msg);

    /* build XML tree */
    *data = lyxml_parse_mem(session->ctx, msg, 0);
    if (!*data) {
        goto malformed_msg;
    } else if (!(*data)->ns) {
        ERR("Session %u: invalid message root element (invalid namespace).", session->id);
        goto malformed_msg;
    }
    free(msg);
    msg = NULL;

    /* get and return message type */
    if (!strcmp((*data)->ns->value, NC_NS_BASE)) {
        if (!strcmp((*data)->name, "rpc")) {
            return NC_MSG_RPC;
        } else if (!strcmp((*data)->name, "rpc-reply")) {
            return NC_MSG_REPLY;
        } else if (!strcmp((*data)->name, "hello")) {
            return NC_MSG_HELLO;
        } else {
            ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
            goto malformed_msg;
        }
    } else if (!strcmp((*data)->ns->value, NC_NS_NOTIF)) {
        if (!strcmp((*data)->name, "notification")) {
            return NC_MSG_NOTIF;
        } else {
            ERR("Session %u: invalid message root element (invalid name \"%s\").", session->id, (*data)->name);
            goto malformed_msg;
        }
    } else {
        ERR("Session %u: invalid message root element (invalid namespace \"%s\").", session->id, (*data)->ns->value);
        goto malformed_msg;
    }

malformed_msg:
    ERR("Session %u: malformed message received.", session->id);
    if ((session->side == NC_SERVER) && (session->version == NC_VERSION_11)) {
        /* NETCONF version 1.1 defines sending error reply from the server (RFC 6241 sec. 3) */
        reply = nc_server_reply_err(nc_err(NC_ERR_MALFORMED_MSG));

        if (io_locked) {
            /* nc_write_msg_io locks and unlocks the lock by itself */
            nc_session_io_unlock(session, __func__);
            io_locked = 0;
        }

        if (nc_write_msg_io(session, io_timeout, NC_MSG_REPLY, NULL, reply) != NC_MSG_REPLY) {
            ERR("Session %u: unable to send a \"Malformed message\" error reply, terminating session.", session->id);
            if (session->status != NC_STATUS_INVALID) {
                session->status = NC_STATUS_INVALID;
                session->term_reason = NC_SESSION_TERM_OTHER;
            }
        }
        nc_server_reply_free(reply);
    }
    ret = NC_MSG_ERROR;

cleanup:
    if (io_locked) {
        nc_session_io_unlock(session, __func__);
    }
    free(msg);
    free(*data);
    *data = NULL;

    return ret;
}