all CHANGE larger api change to enable server stats collecting
All the information required by ietf-netconf-monitoring
model can now be collected.
diff --git a/src/netconf.h b/src/netconf.h
index 201dc56..4b13cd1 100644
--- a/src/netconf.h
+++ b/src/netconf.h
@@ -60,6 +60,7 @@
NC_MSG_WOULDBLOCK, /**< timeout return value */
NC_MSG_NONE, /**< no message at input or message was processed internally */
NC_MSG_HELLO, /**< \<hello\> message */
+ NC_MSG_BAD_HELLO, /**< \<hello\> message parsing failed */
NC_MSG_RPC, /**< \<rpc\> message */
NC_MSG_REPLY, /**< \<rpc-reply\> message */
NC_MSG_REPLY_ERR_MSGID, /**< \<rpc-reply\> message with missing or wrong message-id attribute value */
diff --git a/src/session.c b/src/session.c
index 92df1d2..80ec526 100644
--- a/src/session.c
+++ b/src/session.c
@@ -789,12 +789,16 @@
goto error;
}
break;
+ case NC_MSG_WOULDBLOCK:
+ ERR("Server's <hello> timeout elapsed.");
+ break;
case NC_MSG_ERROR:
/* nothing special, just pass it out */
break;
default:
ERR("Unexpected message received instead of <hello>.");
msgtype = NC_MSG_ERROR;
+ break;
}
/* cleanup */
@@ -813,7 +817,7 @@
nc_recv_server_hello(struct nc_session *session)
{
struct lyxml_elem *xml = NULL, *node;
- NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
+ NC_MSG_TYPE msgtype;
int ver = -1;
int flag = 0;
@@ -827,20 +831,20 @@
continue;
} else if (strcmp(node->name, "capabilities")) {
ERR("Unexpected <%s> element in client's <hello>.", node->name);
- msgtype = NC_MSG_ERROR;
+ msgtype = NC_MSG_BAD_HELLO;
goto cleanup;
}
if (flag) {
/* multiple capabilities elements */
ERR("Invalid <hello> message (multiple <capabilities> elements).");
- msgtype = NC_MSG_ERROR;
+ msgtype = NC_MSG_BAD_HELLO;
goto cleanup;
}
flag = 1;
if ((ver = parse_cpblts(node, NULL)) < 0) {
- msgtype = NC_MSG_ERROR;
+ msgtype = NC_MSG_BAD_HELLO;
goto cleanup;
}
session->version = ver;
@@ -851,11 +855,11 @@
break;
case NC_MSG_WOULDBLOCK:
ERR("Client's <hello> timeout elapsed.");
- msgtype = NC_MSG_ERROR;
break;
default:
ERR("Unexpected message received instead of <hello>.");
msgtype = NC_MSG_ERROR;
+ break;
}
cleanup:
@@ -864,7 +868,7 @@
return msgtype;
}
-int
+NC_MSG_TYPE
nc_handshake(struct nc_session *session)
{
NC_MSG_TYPE type;
@@ -876,7 +880,7 @@
}
if (type != NC_MSG_HELLO) {
- return 1;
+ return type;
}
if (session->side == NC_CLIENT) {
@@ -885,11 +889,7 @@
type = nc_recv_server_hello(session);
}
- if (type != NC_MSG_HELLO) {
- return 1;
- }
-
- return 0;
+ return type;
}
#ifdef NC_ENABLED_SSH
diff --git a/src/session_client.c b/src/session_client.c
index d675240..3991fab 100644
--- a/src/session_client.c
+++ b/src/session_client.c
@@ -372,7 +372,7 @@
session->ctx = ctx;
/* NETCONF handshake */
- if (nc_handshake(session)) {
+ if (nc_handshake(session) != NC_MSG_HELLO) {
goto fail;
}
session->status = NC_STATUS_RUNNING;
diff --git a/src/session_client_ssh.c b/src/session_client_ssh.c
index 8d810ab..3d93ff0 100644
--- a/src/session_client_ssh.c
+++ b/src/session_client_ssh.c
@@ -1380,7 +1380,7 @@
session->ctx = ctx;
/* NETCONF handshake */
- if (nc_handshake(session)) {
+ if (nc_handshake(session) != NC_MSG_HELLO) {
goto fail;
}
session->status = NC_STATUS_RUNNING;
@@ -1510,7 +1510,7 @@
session->ctx = ctx;
/* NETCONF handshake */
- if (nc_handshake(session)) {
+ if (nc_handshake(session) != NC_MSG_HELLO) {
goto fail;
}
session->status = NC_STATUS_RUNNING;
@@ -1582,7 +1582,7 @@
new_session->ctx = ctx;
/* NETCONF handshake */
- if (nc_handshake(new_session)) {
+ if (nc_handshake(new_session) != NC_MSG_HELLO) {
goto fail;
}
new_session->status = NC_STATUS_RUNNING;
diff --git a/src/session_client_tls.c b/src/session_client_tls.c
index 25ddd85..649614b 100644
--- a/src/session_client_tls.c
+++ b/src/session_client_tls.c
@@ -572,7 +572,7 @@
session->ctx = ctx;
/* NETCONF handshake */
- if (nc_handshake(session)) {
+ if (nc_handshake(session) != NC_MSG_HELLO) {
goto fail;
}
session->status = NC_STATUS_RUNNING;
@@ -644,7 +644,7 @@
session->ctx = ctx;
/* NETCONF handshake */
- if (nc_handshake(session)) {
+ if (nc_handshake(session) != NC_MSG_HELLO) {
goto fail;
}
session->status = NC_STATUS_RUNNING;
diff --git a/src/session_p.h b/src/session_p.h
index 8464222..6ca7ef3 100644
--- a/src/session_p.h
+++ b/src/session_p.h
@@ -315,9 +315,10 @@
* @brief Perform NETCONF handshake on \p session.
*
* @param[in] session NETCONF session to use.
- * @return 0 on success, non-zero on failure.
+ * @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message parsing fail
+ * (server-side only), NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other error.
*/
-int nc_handshake(struct nc_session *session);
+NC_MSG_TYPE nc_handshake(struct nc_session *session);
/**
* @brief Create a socket connection.
@@ -437,9 +438,10 @@
* @param[in] port Port to connect to.
* @param[in] ti Transport fo the connection.
* @param[out] session New Call Home session.
- * @return 0 on success, -1 on error.
+ * @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
+ * parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
*/
-int nc_connect_callhome(const char *host, uint16_t port, NC_TRANSPORT_IMPL ti, struct nc_session **session);
+NC_MSG_TYPE nc_connect_callhome(const char *host, uint16_t port, NC_TRANSPORT_IMPL ti, struct nc_session **session);
void nc_init(void);
@@ -485,13 +487,13 @@
*
* @param[in] session NETCONF session communicating on the socket.
* @param[in,out] timeout Timeout for locking ti_lock.
- * @return 0 - timeout,
- * 1 if \p session channel has data,
- * 2 if some other channel has data,
- * 3 on \p session status change,
- * 4 on new SSH message,
- * 5 on new NETCONF SSH channel,
- * -1 on error.
+ * @return NC_PSPOLL_TIMEOUT,
+ * NC_PSPOLL_RPC (has new data),
+ * NC_PSPOLL_PENDING (other channel has data),
+ * NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR,
+ * NC_PSPOLL_SSH_MSG,
+ * NC_PSPOLL_SSH_CHANNEL,
+ * NC_PSPOLL_ERROR.
*/
int nc_ssh_pollin(struct nc_session *session, int timeout);
diff --git a/src/session_server.c b/src/session_server.c
index 47faab2..478ebfa 100644
--- a/src/session_server.c
+++ b/src/session_server.c
@@ -467,31 +467,33 @@
return server_opts.idle_timeout;
}
-API int
+API NC_MSG_TYPE
nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
{
+ NC_MSG_TYPE msgtype;
+
if (!server_opts.ctx) {
ERRINIT;
- return -1;
+ return NC_MSG_ERROR;
} else if (fdin < 0) {
ERRARG("fdin");
- return -1;
+ return NC_MSG_ERROR;
} else if (fdout < 0) {
ERRARG("fdout");
- return -1;
+ return NC_MSG_ERROR;
} else if (!username) {
ERRARG("username");
- return -1;
+ return NC_MSG_ERROR;
} else if (!session) {
ERRARG("session");
- return -1;
+ return NC_MSG_ERROR;
}
/* prepare session structure */
*session = calloc(1, sizeof **session);
if (!(*session)) {
ERRMEM;
- return -1;
+ return NC_MSG_ERROR;
}
(*session)->status = NC_STATUS_STARTING;
(*session)->side = NC_SERVER;
@@ -511,18 +513,16 @@
pthread_spin_unlock(&server_opts.sid_lock);
/* NETCONF handshake */
- if (nc_handshake(*session)) {
- goto fail;
+ msgtype = nc_handshake(*session);
+ if (msgtype != NC_MSG_HELLO) {
+ nc_session_free(*session, NULL);
+ *session = NULL;
+ return msgtype;
}
(*session)->session_start = (*session)->last_rpc = time(NULL);
(*session)->status = NC_STATUS_RUNNING;
- return 0;
-
-fail:
- nc_session_free(*session, NULL);
- *session = NULL;
- return -1;
+ return msgtype;
}
int
@@ -787,8 +787,13 @@
return count;
}
-/* must be called holding the session lock! */
-static NC_MSG_TYPE
+/* must be called holding the session lock!
+ * returns: NC_PSPOLL_ERROR,
+ * NC_PSPOLL_BAD_RPC,
+ * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
+ * NC_PSPOLL_RPC
+ */
+static int
nc_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
{
struct lyxml_elem *xml = NULL;
@@ -798,13 +803,13 @@
if (!session) {
ERRARG("session");
- return NC_MSG_ERROR;
+ return NC_PSPOLL_ERROR;
} else if (!rpc) {
ERRARG("rpc");
- return NC_MSG_ERROR;
+ return NC_PSPOLL_ERROR;
} else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
ERR("Session %u: invalid session to receive RPCs.", session->id);
- return NC_MSG_ERROR;
+ return NC_PSPOLL_ERROR;
}
msgtype = nc_read_msg(session, &xml);
@@ -826,49 +831,58 @@
nc_server_reply_free(reply);
if (ret == -1) {
ERR("Session %u: failed to write reply.", session->id);
- msgtype = NC_MSG_ERROR;
- } else {
- msgtype = NC_MSG_NONE;
}
+ ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
+ } else {
+ ret = NC_PSPOLL_RPC;
}
(*rpc)->root = xml;
break;
case NC_MSG_HELLO:
ERR("Session %u: received another <hello> message.", session->id);
+ ret = NC_PSPOLL_BAD_RPC;
goto error;
case NC_MSG_REPLY:
ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
+ ret = NC_PSPOLL_BAD_RPC;
goto error;
case NC_MSG_NOTIF:
ERR("Session %u: received <notification> from a NETCONF client.", session->id);
+ ret = NC_PSPOLL_BAD_RPC;
goto error;
default:
- /* NC_MSG_ERROR - pass it out;
+ /* NC_MSG_ERROR,
* NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
*/
+ ret = NC_PSPOLL_ERROR;
break;
}
- return msgtype;
+ return ret;
error:
/* cleanup */
lyxml_free(server_opts.ctx, xml);
- return NC_MSG_ERROR;
+ return NC_PSPOLL_ERROR;
}
-/* must be called holding the session lock! */
-static NC_MSG_TYPE
+/* must be called holding the session lock!
+ * returns: NC_PSPOLL_ERROR,
+ * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
+ * NC_PSPOLL_REPLY_ERROR,
+ * 0
+ */
+static int
nc_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
{
nc_rpc_clb clb;
struct nc_server_reply *reply;
- int ret;
+ int ret = 0, r;
if (!rpc) {
ERRINT;
- return NC_MSG_ERROR;
+ return NC_PSPOLL_ERROR;
}
/* no callback, reply with a not-implemented error */
@@ -882,50 +896,53 @@
if (!reply) {
reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
}
+ r = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
+ if (reply->type == NC_RPL_ERROR) {
+ ret |= NC_PSPOLL_REPLY_ERROR;
+ }
+ nc_server_reply_free(reply);
- ret = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
+ if (r == -1) {
+ ERR("Session %u: failed to write reply.", session->id);
+ ret |= NC_PSPOLL_ERROR;
+ }
/* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
session->status = NC_STATUS_INVALID;
}
- if (ret == -1) {
- ERR("Session %u: failed to write reply.", session->id);
- nc_server_reply_free(reply);
- return NC_MSG_ERROR;
- }
- nc_server_reply_free(reply);
-
- return NC_MSG_REPLY;
+ return ret;
}
API int
-nc_ps_poll(struct nc_pollsession *ps, int timeout)
+nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
{
int ret;
uint16_t i;
time_t cur_time;
- NC_MSG_TYPE msgtype;
- struct nc_session *session;
+ struct nc_session *cur_session;
struct nc_server_rpc *rpc = NULL;
if (!ps || !ps->session_count) {
ERRARG("ps");
- return -1;
+ return NC_PSPOLL_ERROR;
}
cur_time = time(NULL);
/* LOCK */
if (nc_ps_lock(ps)) {
- return -1;
+ return NC_PSPOLL_ERROR;
}
for (i = 0; i < ps->session_count; ++i) {
if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
ERR("Session %u: session not running.", ps->sessions[i]->id);
- ret = -1;
+ ret = NC_PSPOLL_ERROR;
+ if (session) {
+ *session = ps->sessions[i];
+ }
goto finish;
}
@@ -934,7 +951,10 @@
ERR("Session %u: session idle timeout elapsed.", ps->sessions[i]->id);
ps->sessions[i]->status = NC_STATUS_INVALID;
ps->sessions[i]->term_reason = NC_SESSION_TERM_TIMEOUT;
- ret = 3;
+ ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
+ if (session) {
+ *session = ps->sessions[i];
+ }
goto finish;
}
@@ -950,7 +970,12 @@
/* no leftover event */
i = 0;
ret = poll(ps->pfds, ps->session_count, timeout);
- if (ret < 1) {
+ if (ret < 0) {
+ ERR("Poll failed (%s).", strerror(errno));
+ ret = NC_PSPOLL_ERROR;
+ goto finish;
+ } else if (!ret) {
+ ret = NC_PSPOLL_TIMEOUT;
goto finish;
}
}
@@ -961,13 +986,19 @@
ERR("Session %u: communication socket unexpectedly closed.", ps->sessions[i]->id);
ps->sessions[i]->status = NC_STATUS_INVALID;
ps->sessions[i]->term_reason = NC_SESSION_TERM_DROPPED;
- ret = 3;
+ ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
+ if (session) {
+ *session = ps->sessions[i];
+ }
goto finish;
} else if (ps->pfds[i].revents & POLLERR) {
ERR("Session %u: communication socket error.", ps->sessions[i]->id);
ps->sessions[i]->status = NC_STATUS_INVALID;
ps->sessions[i]->term_reason = NC_SESSION_TERM_OTHER;
- ret = 3;
+ ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
+ if (session) {
+ *session = ps->sessions[i];
+ }
goto finish;
} else if (ps->pfds[i].revents & POLLIN) {
#ifdef NC_ENABLED_SSH
@@ -978,7 +1009,7 @@
ret = nc_ssh_pollin(ps->sessions[i], timeout);
/* clear POLLIN on sessions sharing this session's SSH session */
- if ((ret == 1) || (ret >= 4)) {
+ if (ret & (NC_PSPOLL_RPC | NC_PSPOLL_SSH_MSG | NC_PSPOLL_SSH_CHANNEL)) {
for (j = i + 1; j < ps->session_count; ++j) {
if (ps->pfds[j].fd == ps->pfds[i].fd) {
ps->pfds[j].revents = 0;
@@ -986,19 +1017,22 @@
}
}
- /* actual event happened */
- if ((ret <= 0) || (ret >= 3)) {
+ /* SSH message only */
+ if (!(ret & (NC_PSPOLL_RPC | NC_PSPOLL_PENDING))) {
ps->pfds[i].revents = 0;
+ if (session) {
+ *session = ps->sessions[i];
+ }
goto finish;
/* event occurred on some other channel */
- } else if (ret == 2) {
+ } else if (ret & NC_PSPOLL_PENDING) {
ps->pfds[i].revents = 0;
if (i == ps->session_count - 1) {
/* last session and it is not the right channel, ... */
if (!timeout) {
/* ... timeout is 0, so that is it */
- ret = 0;
+ ret = NC_PSPOLL_TIMEOUT;
goto finish;
}
/* ... retry polling reasonable time apart ... */
@@ -1023,69 +1057,58 @@
if (i == ps->session_count) {
ERRINT;
- ret = -1;
+ ret = NC_PSPOLL_ERROR;
goto finish;
}
/* this is the session with some data available for reading */
- session = ps->sessions[i];
+ cur_session = ps->sessions[i];
+ if (session) {
+ *session = cur_session;
+ }
/* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
- ret = nc_timedlock(session->ti_lock, timeout);
- if (ret != 1) {
- /* error or timeout */
+ ret = nc_timedlock(cur_session->ti_lock, timeout);
+ if (ret < 0) {
+ ret = NC_PSPOLL_ERROR;
+ goto finish;
+ } else if (!ret) {
+ ret = NC_PSPOLL_TIMEOUT;
goto finish;
}
- msgtype = nc_recv_rpc(session, &rpc);
- if (msgtype == NC_MSG_ERROR) {
- pthread_mutex_unlock(session->ti_lock);
- if (session->status != NC_STATUS_RUNNING) {
- ret = 3;
- goto finish;
+ ret = nc_recv_rpc(cur_session, &rpc);
+ if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
+ pthread_mutex_unlock(cur_session->ti_lock);
+ if (cur_session->status != NC_STATUS_RUNNING) {
+ ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
}
- ret = -1;
goto finish;
- } else if (msgtype == NC_MSG_NONE) {
- /* already processed, just stop further processing */
- pthread_mutex_unlock(session->ti_lock);
- goto done;
}
- if (msgtype == NC_MSG_RPC) {
- session->last_rpc = time(NULL);
- }
+ cur_session->last_rpc = time(NULL);
/* process RPC */
- msgtype = nc_send_reply(session, rpc);
+ ret |= nc_send_reply(cur_session, rpc);
- pthread_mutex_unlock(session->ti_lock);
-
- if (msgtype == NC_MSG_ERROR) {
- nc_server_rpc_free(rpc, server_opts.ctx);
- ret = -1;
- goto finish;
+ pthread_mutex_unlock(cur_session->ti_lock);
+ if (cur_session->status != NC_STATUS_RUNNING) {
+ ret |= NC_PSPOLL_SESSION_TERM;
+ if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
+ ret |= NC_PSPOLL_SESSION_ERROR;
+ }
}
-done:
nc_server_rpc_free(rpc, server_opts.ctx);
- /* status change takes precedence over leftover events (return 2) */
- if (session->status != NC_STATUS_RUNNING) {
- ret = 3;
- goto finish;
- }
-
/* is there some other socket waiting? */
for (++i; i < ps->session_count; ++i) {
if (ps->pfds[i].revents) {
- ret = 2;
- goto finish;
+ ret |= NC_PSPOLL_PENDING;
+ break;
}
}
- ret = 1;
-
finish:
/* UNLOCK */
nc_ps_unlock(ps);
@@ -1395,19 +1418,20 @@
return ret;
}
-API int
+API NC_MSG_TYPE
nc_accept(int timeout, struct nc_session **session)
{
+ NC_MSG_TYPE msgtype;
int sock, ret;
char *host = NULL;
uint16_t port, idx;
if (!server_opts.ctx) {
ERRINIT;
- return -1;
+ return NC_MSG_ERROR;
} else if (!session) {
ERRARG("session");
- return -1;
+ return NC_MSG_ERROR;
}
/* we have to hold WRITE for the whole time, since there is not
@@ -1419,7 +1443,7 @@
ERRINIT;
/* WRITE UNLOCK */
pthread_rwlock_unlock(&server_opts.endpt_array_lock);
- return -1;
+ return NC_MSG_ERROR;
}
ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &idx);
@@ -1428,7 +1452,7 @@
/* WRITE UNLOCK */
pthread_rwlock_unlock(&server_opts.endpt_array_lock);
free(host);
- return ret;
+ return NC_MSG_ERROR;
}
sock = ret;
@@ -1437,8 +1461,8 @@
ERRMEM;
close(sock);
free(host);
- ret = -1;
- goto fail;
+ msgtype = NC_MSG_ERROR;
+ goto cleanup;
}
(*session)->status = NC_STATUS_STARTING;
(*session)->side = NC_SERVER;
@@ -1452,8 +1476,8 @@
if (!(*session)->ti_lock) {
ERRMEM;
close(sock);
- ret = -1;
- goto fail;
+ msgtype = NC_MSG_ERROR;
+ goto cleanup;
}
pthread_mutex_init((*session)->ti_lock, NULL);
@@ -1463,24 +1487,32 @@
#ifdef NC_ENABLED_SSH
if (server_opts.binds[idx].ti == NC_TI_LIBSSH) {
ret = nc_accept_ssh_session(*session, sock, timeout);
- if (ret < 1) {
- goto fail;
+ if (ret < 0) {
+ msgtype = NC_MSG_ERROR;
+ goto cleanup;
+ } else if (!ret) {
+ msgtype = NC_MSG_WOULDBLOCK;
+ goto cleanup;
}
} else
#endif
#ifdef NC_ENABLED_TLS
if (server_opts.binds[idx].ti == NC_TI_OPENSSL) {
ret = nc_accept_tls_session(*session, sock, timeout);
- if (ret < 1) {
- goto fail;
+ if (ret < 0) {
+ msgtype = NC_MSG_ERROR;
+ goto cleanup;
+ } else if (!ret) {
+ msgtype = NC_MSG_WOULDBLOCK;
+ goto cleanup;
}
} else
#endif
{
ERRINT;
close(sock);
- ret = -1;
- goto fail;
+ msgtype = NC_MSG_ERROR;
+ goto cleanup;
}
(*session)->data = NULL;
@@ -1496,54 +1528,56 @@
pthread_spin_unlock(&server_opts.sid_lock);
/* NETCONF handshake */
- if (nc_handshake(*session)) {
+ msgtype = nc_handshake(*session);
+ if (msgtype != NC_MSG_HELLO) {
nc_session_free(*session, NULL);
*session = NULL;
- return -1;
+ return msgtype;
}
(*session)->session_start = time(NULL);
(*session)->status = NC_STATUS_RUNNING;
- return 1;
+ return msgtype;
-fail:
+cleanup:
/* WRITE UNLOCK */
pthread_rwlock_unlock(&server_opts.endpt_array_lock);
nc_session_free(*session, NULL);
*session = NULL;
- return ret;
+ return msgtype;
}
-int
+NC_MSG_TYPE
nc_connect_callhome(const char *host, uint16_t port, NC_TRANSPORT_IMPL ti, struct nc_session **session)
{
+ NC_MSG_TYPE msgtype;
int sock, ret;
if (!host) {
ERRARG("host");
- return -1;
+ return NC_MSG_ERROR;
} else if (!port) {
ERRARG("port");
- return -1;
+ return NC_MSG_ERROR;
} else if (!ti) {
ERRARG("ti");
- return -1;
+ return NC_MSG_ERROR;
} else if (!session) {
ERRARG("session");
- return -1;
+ return NC_MSG_ERROR;
}
sock = nc_sock_connect(host, port);
if (sock < 0) {
- return -1;
+ return NC_MSG_ERROR;
}
*session = calloc(1, sizeof **session);
if (!(*session)) {
ERRMEM;
close(sock);
- return -1;
+ return NC_MSG_ERROR;
}
(*session)->status = NC_STATUS_STARTING;
(*session)->side = NC_SERVER;
@@ -1557,7 +1591,7 @@
if (!(*session)->ti_lock) {
ERRMEM;
close(sock);
- ret = -1;
+ msgtype = NC_MSG_ERROR;
goto fail;
}
pthread_mutex_init((*session)->ti_lock, NULL);
@@ -1575,7 +1609,11 @@
/* OPTS UNLOCK */
pthread_mutex_unlock(&ssh_ch_opts_lock);
- if (ret < 1) {
+ if (ret < 0) {
+ msgtype = NC_MSG_ERROR;
+ goto fail;
+ } else if (!ret) {
+ msgtype = NC_MSG_WOULDBLOCK;
goto fail;
}
} else
@@ -1592,7 +1630,11 @@
/* OPTS UNLOCK */
pthread_mutex_unlock(&tls_ch_opts_lock);
- if (ret < 1) {
+ if (ret < 0) {
+ msgtype = NC_MSG_ERROR;
+ goto fail;
+ } else if (!ret) {
+ msgtype = NC_MSG_WOULDBLOCK;
goto fail;
}
} else
@@ -1600,7 +1642,7 @@
{
ERRINT;
close(sock);
- ret = -1;
+ msgtype = NC_MSG_ERROR;
goto fail;
}
@@ -1612,19 +1654,19 @@
pthread_spin_unlock(&server_opts.sid_lock);
/* NETCONF handshake */
- if (nc_handshake(*session)) {
- ret = -1;
+ msgtype = nc_handshake(*session);
+ if (msgtype != NC_MSG_HELLO) {
goto fail;
}
(*session)->session_start = time(NULL);
(*session)->status = NC_STATUS_RUNNING;
- return 1;
+ return msgtype;
fail:
nc_session_free(*session, NULL);
*session = NULL;
- return ret;
+ return msgtype;
}
#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
diff --git a/src/session_server.h b/src/session_server.h
index c445304..5707e19 100644
--- a/src/session_server.h
+++ b/src/session_server.h
@@ -172,9 +172,10 @@
* @param[in] fdout File descriptor to write (unencrypted) XML data to.
* @param[in] username NETCONF username as provided by the transport protocol.
* @param[out] session New session on success.
- * @return 0 on success, -1 on error.
+ * @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
+ * parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
*/
-int nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session);
+NC_MSG_TYPE nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session);
/**
* @brief Create an empty structure for polling sessions.
@@ -219,29 +220,34 @@
*/
uint16_t nc_ps_session_count(struct nc_pollsession *ps);
+#define NC_PSPOLL_TIMEOUT 0x0001 /**< Timeout elapsed. */
+#define NC_PSPOLL_RPC 0x0002 /**< RPC was correctly parsed and processed. */
+#define NC_PSPOLL_BAD_RPC 0x0004 /**< RPC was received, but failed to be parsed. */
+#define NC_PSPOLL_REPLY_ERROR 0x0008 /**< Response to the RPC was a \<rpc-reply\> of type error. */
+#define NC_PSPOLL_SESSION_TERM 0x0010 /**< Some session was terminated. */
+#define NC_PSPOLL_SESSION_ERROR 0x0020 /**< Some session was terminated incorrectly (not by \<close-session\> or \<kill-session\> RPCs. */
+#define NC_PSPOLL_PENDING 0x0040 /**< Unhandled pending events on other session. */
+#define NC_PSPOLL_ERROR 0x0080 /**< Other fatal errors (they are printed). */
+
+#ifdef NC_ENABLED_SSH
+# define NC_PSPOLL_SSH_MSG 0x0100 /**< SSH message received (and processed, if relevant, only with SSH support). */
+# define NC_PSPOLL_SSH_CHANNEL 0x0200 /**< New SSH channel opened on an existing session (only with SSH support). */
+#endif
+
/**
* @brief Poll sessions and process any received RPCs.
*
- * All the sessions must be running. If a session fails causing it to change its
- * status, it can be learnt from the return value. Only one event on one session
+ * All the sessions must be running. Only one event on one session
* is handled in one function call.
*
* @param[in] ps Pollsession structure to use.
* @param[in] timeout Poll timeout in milliseconds. 0 for non-blocking call, -1 for
* infinite waiting.
- * @return 0 on elapsed timeout,
- * 1 if an RPC was processed (even if it was not known - it failed to be
- * parsed into session ctx),
- * 2 if an RPC was processed and there are unhandled events on other sessions,
- * 3 if a session from \p ps changed its status (was invalidated),
- * -1 on error (a session likely changed its status as well).
- *
- * Only with SSH support:
- * 4 if an SSH message was processed,
- * 5 if a new NETCONF SSH channel was created; call nc_ps_accept_ssh_channel()
- * to establish a new NETCONF session.
+ * @param[in] session Session that was processed and that specific return bits concern.
+ * Can be NULL.
+ * @return Bitfield of NC_PSPOLL_* macros, almost any combination can be returned.
*/
-int nc_ps_poll(struct nc_pollsession *ps, int timeout);
+int nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session);
/**
* @brief Remove sessions from a pollsession structure and
@@ -261,26 +267,39 @@
* @brief Accept new sessions on all the listening endpoints.
*
* @param[in] timeout Timeout for receiving a new connection in milliseconds, 0 for
- * non-blocking call, -1 for infinite waiting.
+ * non-blocking call, -1 for infinite waiting.
* @param[out] session New session.
- * @return 1 on success, 0 on timeout, -1 on error.
+ * @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
+ * parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
*/
-int nc_accept(int timeout, struct nc_session **session);
+NC_MSG_TYPE nc_accept(int timeout, struct nc_session **session);
#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
#ifdef NC_ENABLED_SSH
/**
+ * @brief Accept a new NETCONF session on an SSH session of a running NETCONF \p orig_session.
+ * Call this function only when nc_ps_poll() returns NC_PSPOLL_SSH_CHANNEL on \p orig_session.
+ *
+ * @param[in] orig_session Session that has a new SSH channel ready.
+ * @param[out] session New session.
+ * @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
+ * parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
+ */
+NC_MSG_TYPE nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session);
+
+/**
* @brief Accept a new NETCONF session on an SSH session of a running NETCONF session
- * that was polled in \p ps. Call this function only when nc_ps_poll() on \p ps returns 5.
+ * that was polled in \p ps. Call this function only when nc_ps_poll() on \p ps returns NC_PSPOLL_SSH_CHANNEL.
* The new session is only returned in \p session, it is not added to \p ps.
*
* @param[in] ps Unmodified pollsession structure from the previous nc_ps_poll() call.
* @param[out] session New session.
- * @return 0 on success, -1 on error.
+ * @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
+ * parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
*/
-int nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session);
+NC_MSG_TYPE nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session);
/**
* @brief Add a new SSH endpoint and start listening on it.
diff --git a/src/session_server_ch.h b/src/session_server_ch.h
index 161a44f..79eca8c 100644
--- a/src/session_server_ch.h
+++ b/src/session_server_ch.h
@@ -29,9 +29,10 @@
* @param[in] host Host the client is listening on.
* @param[in] port Port the client is listening on.
* @param[out] session New Call Home session.
- * @return 1 on success, 0 on timeout, -1 on error.
+ * @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
+ * parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
*/
-int nc_connect_callhome_ssh(const char *host, uint16_t port, struct nc_session **session);
+NC_MSG_TYPE nc_connect_callhome_ssh(const char *host, uint16_t port, struct nc_session **session);
/**
* @brief Set Call Home SSH host keys the server will identify itself with. Each of RSA, DSA, and
@@ -110,9 +111,10 @@
* @param[in] host Host the client is listening on.
* @param[in] port Port the client is listening on.
* @param[out] session New Call Home session.
- * @return 1 on success, 0 on timeout, -1 on error.
+ * @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
+ * parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
*/
-int nc_connect_callhome_tls(const char *host, uint16_t port, struct nc_session **session);
+NC_MSG_TYPE nc_connect_callhome_tls(const char *host, uint16_t port, struct nc_session **session);
/**
* @brief Set server Call Home TLS certificate. Alternative to nc_tls_server_set_cert_path().
diff --git a/src/session_server_ssh.c b/src/session_server_ssh.c
index d59e87a..409a83b 100644
--- a/src/session_server_ssh.c
+++ b/src/session_server_ssh.c
@@ -1005,8 +1005,6 @@
return 0;
}
-/* ret 0 - timeout, 1 channel has data, 2 some other channel has data,
- * 3 status change, 4 new SSH message, 5 new NETCONF SSH channel, -1 error */
int
nc_ssh_pollin(struct nc_session *session, int timeout)
{
@@ -1015,8 +1013,10 @@
ret = nc_timedlock(session->ti_lock, timeout);
- if (ret != 1) {
- return ret;
+ if (ret < 0) {
+ return NC_PSPOLL_ERROR;
+ } else if (!ret) {
+ return NC_PSPOLL_TIMEOUT;
}
ret = ssh_execute_message_callbacks(session->ti.libssh.session);
@@ -1027,7 +1027,7 @@
ssh_get_error(session->ti.libssh.session));
session->status = NC_STATUS_INVALID;
session->term_reason = NC_SESSION_TERM_OTHER;
- return 3;
+ return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
}
/* new SSH message */
@@ -1038,38 +1038,38 @@
if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
&& (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
/* new NETCONF SSH channel */
- return 5;
+ return NC_PSPOLL_SSH_CHANNEL;
}
}
}
/* just some SSH message */
- return 4;
+ return NC_PSPOLL_SSH_MSG;
}
/* no new SSH message, maybe NETCONF data? */
ret = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
/* not this one */
if (!ret) {
- return 2;
+ return NC_PSPOLL_PENDING;
} else if (ret == SSH_ERROR) {
ERR("Session %u: SSH channel error (%s).", session->id,
ssh_get_error(session->ti.libssh.session));
session->status = NC_STATUS_INVALID;
session->term_reason = NC_SESSION_TERM_OTHER;
- return 3;
+ return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
} else if (ret == SSH_EOF) {
ERR("Session %u: communication channel unexpectedly closed (libssh).",
session->id);
session->status = NC_STATUS_INVALID;
session->term_reason = NC_SESSION_TERM_DROPPED;
- return 3;
+ return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
}
- return 1;
+ return NC_PSPOLL_RPC;
}
-API int
+API NC_MSG_TYPE
nc_connect_callhome_ssh(const char *host, uint16_t port, struct nc_session **session)
{
return nc_connect_callhome(host, port, NC_TI_LIBSSH, session);
@@ -1170,23 +1170,77 @@
return 1;
}
-API int
+API NC_MSG_TYPE
+nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session)
+{
+ NC_MSG_TYPE msgtype;
+ struct nc_session *new_session = NULL;
+
+ if (!orig_session) {
+ ERRARG("orig_session");
+ return NC_MSG_ERROR;
+ } else if (!session) {
+ ERRARG("session");
+ return NC_MSG_ERROR;
+ }
+
+ if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH)
+ && orig_session->ti.libssh.next) {
+ for (new_session = orig_session->ti.libssh.next;
+ new_session != orig_session;
+ new_session = new_session->ti.libssh.next) {
+ if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
+ && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
+ /* we found our session */
+ break;
+ }
+ }
+ if (new_session == orig_session) {
+ new_session = NULL;
+ }
+ }
+
+ if (!new_session) {
+ ERR("Session does not have a NETCONF SSH channel ready.");
+ return NC_MSG_ERROR;
+ }
+
+ /* assign new SID atomically */
+ pthread_spin_lock(&server_opts.sid_lock);
+ new_session->id = server_opts.new_session_id++;
+ pthread_spin_unlock(&server_opts.sid_lock);
+
+ /* NETCONF handshake */
+ msgtype = nc_handshake(new_session);
+ if (msgtype != NC_MSG_HELLO) {
+ return msgtype;
+ }
+
+ new_session->session_start = time(NULL);
+ new_session->status = NC_STATUS_RUNNING;
+ *session = new_session;
+
+ return msgtype;
+}
+
+API NC_MSG_TYPE
nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
{
+ NC_MSG_TYPE msgtype;
struct nc_session *new_session = NULL;
uint16_t i;
if (!ps) {
ERRARG("ps");
- return -1;
+ return NC_MSG_ERROR;
} else if (!session) {
ERRARG("session");
- return -1;
+ return NC_MSG_ERROR;
}
/* LOCK */
if (nc_ps_lock(ps)) {
- return -1;
+ return NC_MSG_ERROR;
}
for (i = 0; i < ps->session_count; ++i) {
@@ -1215,7 +1269,7 @@
if (!new_session) {
ERR("No session with a NETCONF SSH channel ready was found.");
- return -1;
+ return NC_MSG_ERROR;
}
/* assign new SID atomically */
@@ -1224,13 +1278,14 @@
pthread_spin_unlock(&server_opts.sid_lock);
/* NETCONF handshake */
- if (nc_handshake(new_session)) {
- return -1;
+ msgtype = nc_handshake(new_session);
+ if (msgtype != NC_MSG_HELLO) {
+ return msgtype;
}
new_session->session_start = time(NULL);
new_session->status = NC_STATUS_RUNNING;
*session = new_session;
- return 0;
+ return msgtype;
}
diff --git a/src/session_server_tls.c b/src/session_server_tls.c
index 42bb445..cbcd3d5 100644
--- a/src/session_server_tls.c
+++ b/src/session_server_tls.c
@@ -1515,7 +1515,7 @@
pthread_key_create(&verify_key, NULL);
}
-API int
+API NC_MSG_TYPE
nc_connect_callhome_tls(const char *host, uint16_t port, struct nc_session **session)
{
return nc_connect_callhome(host, port, NC_TI_OPENSSL, session);
diff --git a/tests/test_fd_comm.c b/tests/test_fd_comm.c
index 67602b7..7a63760 100644
--- a/tests/test_fd_comm.c
+++ b/tests/test_fd_comm.c
@@ -141,8 +141,8 @@
assert_non_null(ps);
nc_ps_add_session(ps, server_session);
- ret = nc_ps_poll(ps, 0);
- assert_int_equal(ret, 1);
+ ret = nc_ps_poll(ps, 0, NULL);
+ assert_int_equal(ret, NC_PSPOLL_RPC);
/* server finished */
nc_ps_free(ps);
@@ -200,8 +200,8 @@
assert_non_null(ps);
nc_ps_add_session(ps, server_session);
- ret = nc_ps_poll(ps, 0);
- assert_int_equal(ret, 1);
+ ret = nc_ps_poll(ps, 0, NULL);
+ assert_int_equal(ret, NC_PSPOLL_RPC | NC_PSPOLL_REPLY_ERROR);
/* server finished */
nc_ps_free(ps);
@@ -260,8 +260,8 @@
assert_non_null(ps);
nc_ps_add_session(ps, server_session);
- ret = nc_ps_poll(ps, 0);
- assert_int_equal(ret, 1);
+ ret = nc_ps_poll(ps, 0, NULL);
+ assert_int_equal(ret, NC_PSPOLL_RPC);
/* server finished */
nc_ps_free(ps);
diff --git a/tests/test_server_thread.c b/tests/test_server_thread.c
index 5d7d08d..5ba9881 100644
--- a/tests/test_server_thread.c
+++ b/tests/test_server_thread.c
@@ -44,6 +44,7 @@
server_thread(void *arg)
{
(void)arg;
+ NC_MSG_TYPE msgtype;
int ret;
struct nc_pollsession *ps;
struct nc_session *session;
@@ -54,21 +55,21 @@
pthread_barrier_wait(&barrier);
#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
- ret = nc_accept(NC_ACCEPT_TIMEOUT, &session);
- nc_assert(ret == 1);
+ msgtype = nc_accept(NC_ACCEPT_TIMEOUT, &session);
+ nc_assert(msgtype == NC_MSG_HELLO);
nc_ps_add_session(ps, session);
- ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT);
- nc_assert(ret == 3);
+ ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL);
+ nc_assert(ret & NC_PSPOLL_RPC);
nc_ps_clear(ps, 0, NULL);
#endif
- ret = nc_accept(NC_ACCEPT_TIMEOUT, &session);
- nc_assert(ret == 1);
+ msgtype = nc_accept(NC_ACCEPT_TIMEOUT, &session);
+ nc_assert(msgtype == NC_MSG_HELLO);
nc_ps_add_session(ps, session);
- ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT);
- nc_assert(ret == 3);
+ ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL);
+ nc_assert(ret & NC_PSPOLL_RPC);
nc_ps_clear(ps, 0, NULL);
nc_ps_free(ps);