blob: 3306c07c00c1806cd60e8e1217e4f990744d37d2 [file] [log] [blame]
Radek Krejci206fcd62015-10-07 15:42:48 +02001/**
2 * \file session.c
Michal Vasko086311b2016-01-08 09:53:11 +01003 * \author Michal Vasko <mvasko@cesnet.cz>
4 * \brief libnetconf2 - general session functions
Radek Krejci206fcd62015-10-07 15:42:48 +02005 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of the Company nor the names of its contributors
18 * may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 */
22
Radek Krejci206fcd62015-10-07 15:42:48 +020023#include <errno.h>
Radek Krejci952eb862016-01-08 14:22:55 +010024#include <stdlib.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020025#include <pthread.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020026#include <libyang/libyang.h>
27
Michal Vasko086311b2016-01-08 09:53:11 +010028#ifdef ENABLE_SSH
Radek Krejci206fcd62015-10-07 15:42:48 +020029
Michal Vasko086311b2016-01-08 09:53:11 +010030# include <libssh/libssh.h>
Radek Krejci206fcd62015-10-07 15:42:48 +020031
Michal Vasko086311b2016-01-08 09:53:11 +010032#endif /* ENABLE_SSH */
Radek Krejci695d4fa2015-10-22 13:23:54 +020033
Michal Vaskoc14e3c82016-01-11 16:14:30 +010034#ifdef ENABLE_TLS
35
36# include <openssl/err.h>
37
38#endif /* ENABLE_TLS */
39
Michal Vasko086311b2016-01-08 09:53:11 +010040#include "config.h"
41#include "log_p.h"
42#include "session.h"
43#include "session_p.h"
Radek Krejci695d4fa2015-10-22 13:23:54 +020044
Michal Vasko086311b2016-01-08 09:53:11 +010045/* in seconds */
46#define NC_CLIENT_HELLO_TIMEOUT 60
Radek Krejci695d4fa2015-10-22 13:23:54 +020047
Michal Vasko05ba9df2016-01-13 14:40:27 +010048/* in milliseconds */
49#define NC_CLOSE_REPLY_TIMEOUT 200
50
Michal Vasko086311b2016-01-08 09:53:11 +010051extern struct nc_server_opts server_opts;
52
Michal Vasko8dadf782016-01-15 10:29:36 +010053API NC_STATUS
54nc_session_get_status(const struct nc_session *session)
55{
56 return session->status;
57}
58
59API uint32_t
60nc_session_get_id(const struct nc_session *session)
61{
62 return session->id;
63}
64
65API NC_TRANSPORT_IMPL
66nc_session_get_ti(const struct nc_session *session)
67{
68 return session->ti_type;
69}
70
71API const char *
72nc_session_get_username(const struct nc_session *session)
73{
74 return session->username;
75}
76
77API const char *
78nc_session_get_host(const struct nc_session *session)
79{
80 return session->host;
81}
82
83API uint16_t
84nc_session_get_port(const struct nc_session *session)
85{
86 return session->port;
87}
88
89API const char **
90nc_session_get_cpblts(const struct nc_session *session)
91{
92 return session->cpblts;
93}
94
95API const char *
96nc_session_cpblt(const struct nc_session *session, const char *capab)
97{
98 int i, len;
99
100 len = strlen(capab);
101 for (i = 0; session->cpblts[i]; ++i) {
102 if (!strncmp(session->cpblts[i], capab, len)) {
103 return session->cpblts[i];
104 }
105 }
106
107 return NULL;
108}
109
Michal Vasko086311b2016-01-08 09:53:11 +0100110NC_MSG_TYPE
111nc_send_msg(struct nc_session *session, struct lyd_node *op)
Radek Krejci695d4fa2015-10-22 13:23:54 +0200112{
Michal Vasko086311b2016-01-08 09:53:11 +0100113 int r;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200114
Michal Vasko086311b2016-01-08 09:53:11 +0100115 if (session->ctx != op->schema->module->ctx) {
116 ERR("RPC \"%s\" was created in different context than that of \"%s\" session %u.",
117 op->schema->name, session->username, session->id);
118 return NC_MSG_ERROR;
Michal Vasko7df39ec2015-12-09 15:26:24 +0100119 }
120
Michal Vasko086311b2016-01-08 09:53:11 +0100121 r = nc_write_msg(session, NC_MSG_RPC, op, NULL);
122
123 if (r) {
124 return NC_MSG_ERROR;
125 }
126
127 return NC_MSG_RPC;
Michal Vasko7df39ec2015-12-09 15:26:24 +0100128}
129
Radek Krejci5686ff72015-10-09 13:33:56 +0200130/*
131 * @return 0 - success
132 * -1 - timeout
133 * >0 - error
134 */
Michal Vasko086311b2016-01-08 09:53:11 +0100135int
Michal Vaskoc111ac52015-12-08 14:36:36 +0100136session_ti_lock(struct nc_session *session, int32_t timeout)
Radek Krejci206fcd62015-10-07 15:42:48 +0200137{
138 int r;
Radek Krejci206fcd62015-10-07 15:42:48 +0200139
140 if (timeout >= 0) {
141 /* limited waiting for lock */
142 do {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200143 r = pthread_mutex_trylock(session->ti_lock);
Radek Krejci206fcd62015-10-07 15:42:48 +0200144 if (r == EBUSY) {
145 /* try later until timeout passes */
Michal Vasko086311b2016-01-08 09:53:11 +0100146 usleep(NC_TIMEOUT_STEP);
147 timeout = timeout - NC_TIMEOUT_STEP;
Radek Krejci206fcd62015-10-07 15:42:48 +0200148 continue;
149 } else if (r) {
150 /* error */
151 ERR("Acquiring session (%u) TI lock failed (%s).", session->id, strerror(r));
Radek Krejci5686ff72015-10-09 13:33:56 +0200152 return r;
Radek Krejci206fcd62015-10-07 15:42:48 +0200153 } else {
154 /* lock acquired */
Radek Krejci5686ff72015-10-09 13:33:56 +0200155 return 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200156 }
Michal Vasko428087d2016-01-14 16:04:28 +0100157 } while (timeout > 0);
Radek Krejci206fcd62015-10-07 15:42:48 +0200158
Radek Krejci5686ff72015-10-09 13:33:56 +0200159 /* timeout has passed */
160 return -1;
Radek Krejci206fcd62015-10-07 15:42:48 +0200161 } else {
162 /* infinite waiting for lock */
Radek Krejci695d4fa2015-10-22 13:23:54 +0200163 return pthread_mutex_lock(session->ti_lock);
Radek Krejci5686ff72015-10-09 13:33:56 +0200164 }
165}
166
Michal Vasko086311b2016-01-08 09:53:11 +0100167int
Radek Krejci5686ff72015-10-09 13:33:56 +0200168session_ti_unlock(struct nc_session *session)
169{
Radek Krejci695d4fa2015-10-22 13:23:54 +0200170 return pthread_mutex_unlock(session->ti_lock);
171}
172
Radek Krejci695d4fa2015-10-22 13:23:54 +0200173API void
174nc_session_free(struct nc_session *session)
175{
176 int r, i;
Michal Vasko428087d2016-01-14 16:04:28 +0100177 int connected; /* flag to indicate whether the transport socket is still connected */
Radek Krejci695d4fa2015-10-22 13:23:54 +0200178 int multisession = 0; /* flag for more NETCONF session on a single SSH session */
179 struct nc_session *siter;
Michal Vaskoad611702015-12-03 13:41:51 +0100180 struct nc_msg_cont *contiter;
Michal Vaskoadd4c792015-10-26 15:36:58 +0100181 struct lyxml_elem *rpl, *child;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200182 struct lyd_node *close_rpc;
Michal Vaskoad611702015-12-03 13:41:51 +0100183 const struct lys_module *ietfnc;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200184 void *p;
185
Michal Vasko428087d2016-01-14 16:04:28 +0100186 if (!session || (session->status == NC_STATUS_CLOSING)) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200187 return;
188 }
189
190 /* mark session for closing */
Michal Vaskoadd4c792015-10-26 15:36:58 +0100191 if (session->ti_lock) {
192 do {
193 r = session_ti_lock(session, 0);
194 } while (r < 0);
195 if (r) {
196 return;
197 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200198 }
199
200 /* stop notifications loop if any */
201 if (session->notif) {
202 pthread_cancel(*session->notif);
203 pthread_join(*session->notif, NULL);
204 }
205
Michal Vasko428087d2016-01-14 16:04:28 +0100206 if ((session->side == NC_CLIENT) && (session->status == NC_STATUS_RUNNING)) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200207 /* cleanup message queues */
208 /* notifications */
Michal Vaskoad611702015-12-03 13:41:51 +0100209 for (contiter = session->notifs; contiter; ) {
210 lyxml_free(session->ctx, contiter->msg);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200211
Michal Vaskoad611702015-12-03 13:41:51 +0100212 p = contiter;
213 contiter = contiter->next;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200214 free(p);
215 }
216
217 /* rpc replies */
Michal Vaskoad611702015-12-03 13:41:51 +0100218 for (contiter = session->replies; contiter; ) {
219 lyxml_free(session->ctx, contiter->msg);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200220
Michal Vaskoad611702015-12-03 13:41:51 +0100221 p = contiter;
222 contiter = contiter->next;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200223 free(p);
224 }
225
226 /* send closing info to the other side */
227 ietfnc = ly_ctx_get_module(session->ctx, "ietf-netconf", NULL);
228 if (!ietfnc) {
Michal Vasko428087d2016-01-14 16:04:28 +0100229 WRN("Session %u: missing ietf-netconf schema in context, unable to send <close-session>.", session->id);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200230 } else {
231 close_rpc = lyd_new(NULL, ietfnc, "close-session");
Michal Vaskoad611702015-12-03 13:41:51 +0100232 nc_send_msg(session, close_rpc);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200233 lyd_free(close_rpc);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100234 switch (nc_read_msg_poll(session, NC_CLOSE_REPLY_TIMEOUT, &rpl)) {
Michal Vaskofad6e912015-10-26 15:37:22 +0100235 case NC_MSG_REPLY:
236 LY_TREE_FOR(rpl->child, child) {
237 if (!strcmp(child->name, "ok") && child->ns && !strcmp(child->ns->value, NC_NS_BASE)) {
238 break;
239 }
240 }
241 if (!child) {
Michal Vasko428087d2016-01-14 16:04:28 +0100242 WRN("Session %u: the reply to <close-session> was not <ok> as expected.", session->id);
Michal Vaskofad6e912015-10-26 15:37:22 +0100243 }
Michal Vaskoad611702015-12-03 13:41:51 +0100244 lyxml_free(session->ctx, rpl);
Michal Vaskofad6e912015-10-26 15:37:22 +0100245 break;
246 case NC_MSG_WOULDBLOCK:
Michal Vasko428087d2016-01-14 16:04:28 +0100247 WRN("Session %u: timeout for receiving a reply to <close-session> elapsed.", session->id);
Michal Vaskofad6e912015-10-26 15:37:22 +0100248 break;
249 case NC_MSG_ERROR:
Michal Vasko428087d2016-01-14 16:04:28 +0100250 ERR("%s: session %u: failed to receive a reply to <close-session>.", __func__, session->id);
Michal Vaskofad6e912015-10-26 15:37:22 +0100251 break;
252 default:
253 /* cannot happen */
254 break;
255 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200256 }
257
258 /* list of server's capabilities */
259 if (session->cpblts) {
260 for (i = 0; session->cpblts[i]; i++) {
261 lydict_remove(session->ctx, session->cpblts[i]);
262 }
263 free(session->cpblts);
264 }
265 }
266
267 session->status = NC_STATUS_CLOSING;
Michal Vasko428087d2016-01-14 16:04:28 +0100268 connected = nc_session_is_connected(session);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200269
270 /* transport implementation cleanup */
271 switch (session->ti_type) {
272 case NC_TI_FD:
273 /* nothing needed - file descriptors were provided by caller,
274 * so it is up to the caller to close them correctly
275 * TODO use callbacks
276 */
277 break;
278
Michal Vaskofb2fb762015-10-27 11:44:32 +0100279#ifdef ENABLE_SSH
Radek Krejci695d4fa2015-10-22 13:23:54 +0200280 case NC_TI_LIBSSH:
Michal Vasko428087d2016-01-14 16:04:28 +0100281 if (connected) {
282 ssh_channel_free(session->ti.libssh.channel);
283 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200284 /* There can be multiple NETCONF sessions on the same SSH session (NETCONF session maps to
285 * SSH channel). So destroy the SSH session only if there is no other NETCONF session using
286 * it.
287 */
288 if (!session->ti.libssh.next) {
Michal Vasko428087d2016-01-14 16:04:28 +0100289 if (connected) {
290 ssh_disconnect(session->ti.libssh.session);
291 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200292 ssh_free(session->ti.libssh.session);
293 } else {
294 /* multiple NETCONF sessions on a single SSH session */
295 multisession = 1;
296 /* remove the session from the list */
297 for (siter = session->ti.libssh.next; siter->ti.libssh.next != session; siter = siter->ti.libssh.next);
Michal Vaskoaec4f212015-10-26 15:37:45 +0100298 if (session->ti.libssh.next == siter) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200299 /* there will be only one session */
300 siter->ti.libssh.next = NULL;
301 } else {
302 /* there are still multiple sessions, keep the ring list */
303 siter->ti.libssh.next = session->ti.libssh.next;
304 }
305 }
306 break;
307#endif
308
309#ifdef ENABLE_TLS
310 case NC_TI_OPENSSL:
Michal Vasko428087d2016-01-14 16:04:28 +0100311 if (connected) {
312 SSL_shutdown(session->ti.tls);
313 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200314 SSL_free(session->ti.tls);
Michal Vasko06e22432016-01-15 10:30:06 +0100315
316 X509_free(session->tls_cert);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200317 break;
318#endif
Michal Vasko428087d2016-01-14 16:04:28 +0100319 case NC_TI_NONE:
320 ERRINT;
321 break;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200322 }
Michal Vasko428087d2016-01-14 16:04:28 +0100323
Radek Krejciac6d3472015-10-22 15:47:18 +0200324 lydict_remove(session->ctx, session->username);
325 lydict_remove(session->ctx, session->host);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200326
327 /* final cleanup */
Michal Vaskoadd4c792015-10-26 15:36:58 +0100328 if (session->ti_lock) {
329 if (multisession) {
330 session_ti_unlock(session);
331 } else {
332 pthread_mutex_destroy(session->ti_lock);
333 free(session->ti_lock);
334 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200335 }
336
337 if (!(session->flags & NC_SESSION_SHAREDCTX)) {
338 ly_ctx_destroy(session->ctx);
339 }
340
341 free(session);
342}
343
Michal Vasko086311b2016-01-08 09:53:11 +0100344static void
345add_cpblt(struct ly_ctx *ctx, const char *capab, const char ***cpblts, int *size, int *count)
346{
347 if (*count == *size) {
348 *size += 5;
349 *cpblts = realloc(*cpblts, *size * sizeof **cpblts);
350 }
351
352 if (capab) {
353 (*cpblts)[*count] = lydict_insert(ctx, capab, 0);
354 } else {
355 (*cpblts)[*count] = NULL;
356 }
357 ++(*count);
358}
359
360static const char **
361create_cpblts(struct ly_ctx *ctx)
362{
363 struct lyd_node *child, *child2, *yanglib;
364 struct lyd_node_leaf_list **features = NULL, *ns = NULL, *rev = NULL, *name = NULL;
365 const char **cpblts;
366 const struct lys_module *mod;
367 int size = 10, count, feat_count = 0, i;
368 char str[512];
369
370 yanglib = ly_ctx_info(ctx);
371 if (!yanglib) {
372 return NULL;
373 }
374
375 cpblts = malloc(size * sizeof *cpblts);
376 cpblts[0] = lydict_insert(ctx, "urn:ietf:params:netconf:base:1.0", 0);
377 cpblts[1] = lydict_insert(ctx, "urn:ietf:params:netconf:base:1.1", 0);
378 count = 2;
379
380 /* capabilities */
381
382 mod = ly_ctx_get_module(ctx, "ietf-netconf", NULL);
383 if (mod) {
384 if (lys_features_state(mod, "writable-running") == 1) {
385 add_cpblt(ctx, "urn:ietf:params:netconf:writable-running:1.0", &cpblts, &size, &count);
386 }
387 if (lys_features_state(mod, "candidate") == 1) {
388 add_cpblt(ctx, "urn:ietf:params:netconf:candidate:1.0", &cpblts, &size, &count);
389 if (lys_features_state(mod, "confirmed-commit") == 1) {
390 add_cpblt(ctx, "urn:ietf:params:netconf:confirmed-commit:1.1", &cpblts, &size, &count);
391 }
392 }
393 if (lys_features_state(mod, "rollback-on-error") == 1) {
394 add_cpblt(ctx, "urn:ietf:params:netconf:rollback-on-error:1.0", &cpblts, &size, &count);
395 }
396 if (lys_features_state(mod, "validate") == 1) {
397 add_cpblt(ctx, "urn:ietf:params:netconf:validate:1.1", &cpblts, &size, &count);
398 }
399 if (lys_features_state(mod, "startup") == 1) {
400 add_cpblt(ctx, "urn:ietf:params:netconf:startup:1.0", &cpblts, &size, &count);
401 }
402 if (lys_features_state(mod, "url") == 1) {
403 add_cpblt(ctx, "urn:ietf:params:netconf:url:1.0", &cpblts, &size, &count);
404 }
405 if (lys_features_state(mod, "xpath") == 1) {
406 add_cpblt(ctx, "urn:ietf:params:netconf:xpath:1.0", &cpblts, &size, &count);
407 }
408 }
409
410 mod = ly_ctx_get_module(ctx, "ietf-netconf-with-defaults", NULL);
411 if (mod) {
412 if (!server_opts.wd_basic_mode) {
413 VRB("with-defaults capability will not be advertised even though \"ietf-netconf-with-defaults\" model is present, unknown basic-mode.");
414 } else {
415 strcpy(str, "urn:ietf:params:netconf:with-defaults:1.0");
416 switch (server_opts.wd_basic_mode) {
417 case NC_WD_ALL:
418 strcat(str, "?basic-mode=report-all");
419 break;
420 case NC_WD_TRIM:
421 strcat(str, "?basic-mode=trim");
422 break;
423 case NC_WD_EXPLICIT:
424 strcat(str, "?basic-mode=explicit");
425 break;
426 default:
Michal Vasko9e036d52016-01-08 10:49:26 +0100427 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100428 break;
429 }
430
431 if (server_opts.wd_also_supported) {
Radek Krejcif9b28322016-01-08 14:56:43 +0100432 strcat(str, "&amp;also-supported=");
Michal Vasko086311b2016-01-08 09:53:11 +0100433 if (server_opts.wd_also_supported & NC_WD_ALL) {
434 strcat(str, "report-all,");
435 }
436 if (server_opts.wd_also_supported & NC_WD_ALL_TAG) {
437 strcat(str, "report-all-tagged,");
438 }
439 if (server_opts.wd_also_supported & NC_WD_TRIM) {
440 strcat(str, "trim,");
441 }
442 if (server_opts.wd_also_supported & NC_WD_EXPLICIT) {
443 strcat(str, "explicit,");
444 }
445 str[strlen(str) - 1] = '\0';
446
447 add_cpblt(ctx, str, &cpblts, &size, &count);
448 }
449 }
450 }
451
452 mod = ly_ctx_get_module(ctx, "ietf-netconf-notifications", NULL);
453 if (mod) {
454 add_cpblt(ctx, "urn:ietf:params:netconf:notification:1.0", &cpblts, &size, &count);
455 if (server_opts.interleave_capab) {
456 add_cpblt(ctx, "urn:ietf:params:netconf:interleave:1.0", &cpblts, &size, &count);
457 }
458 }
459
460 /* models */
461
462 LY_TREE_FOR(yanglib->child, child) {
463 if (!strcmp(child->schema->name, "module")) {
464 LY_TREE_FOR(child->child, child2) {
465 if (!strcmp(child2->schema->name, "namespace")) {
466 ns = (struct lyd_node_leaf_list *)child2;
467 } else if (!strcmp(child2->schema->name, "name")) {
468 name = (struct lyd_node_leaf_list *)child2;
469 } else if (!strcmp(child2->schema->name, "revision")) {
470 rev = (struct lyd_node_leaf_list *)child2;
471 } else if (!strcmp(child2->schema->name, "feature")) {
472 features = realloc(features, feat_count++ * sizeof *features);
473 features[feat_count - 1] = (struct lyd_node_leaf_list *)child2;
474 }
475 }
476
477 if (!ns || !name || !rev) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100478 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100479 continue;
480 }
481
Radek Krejcif9b28322016-01-08 14:56:43 +0100482 sprintf(str, "%s?module=%s&amp;revision=%s", ns->value_str, name->value_str, rev->value_str);
Michal Vasko086311b2016-01-08 09:53:11 +0100483 if (feat_count) {
Radek Krejcif9b28322016-01-08 14:56:43 +0100484 strcat(str, "&amp;features=");
Michal Vasko086311b2016-01-08 09:53:11 +0100485 for (i = 0; i < feat_count; ++i) {
486 if (i) {
487 strcat(str, ",");
488 }
489 strcat(str, features[i]->value_str);
490 }
491 }
492
493 add_cpblt(ctx, str, &cpblts, &size, &count);
494
495 ns = NULL;
496 name = NULL;
497 rev = NULL;
498 free(features);
499 features = NULL;
500 feat_count = 0;
501 }
502 }
503
504 lyd_free(yanglib);
505
506 /* ending NULL capability */
507 add_cpblt(ctx, NULL, &cpblts, &size, &count);
508
509 return cpblts;
510}
511
Radek Krejci695d4fa2015-10-22 13:23:54 +0200512static int
513parse_cpblts(struct lyxml_elem *xml, const char ***list)
514{
515 struct lyxml_elem *cpblt;
516 int ver = -1;
517 int i = 0;
518
519 if (list) {
520 /* get the storage for server's capabilities */
521 LY_TREE_FOR(xml->child, cpblt) {
522 i++;
523 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100524 /* last item remains NULL */
525 *list = calloc(i + 1, sizeof **list);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200526 if (!*list) {
527 ERRMEM;
528 return -1;
529 }
530 i = 0;
531 }
532
533 LY_TREE_FOR(xml->child, cpblt) {
534 if (strcmp(cpblt->name, "capability") && cpblt->ns && cpblt->ns->value &&
535 !strcmp(cpblt->ns->value, NC_NS_BASE)) {
536 ERR("Unexpected <%s> element in client's <hello>.", cpblt->name);
537 return -1;
538 } else if (!cpblt->ns || !cpblt->ns->value || strcmp(cpblt->ns->value, NC_NS_BASE)) {
539 continue;
540 }
541
542 /* detect NETCONF version */
543 if (ver < 0 && !strcmp(cpblt->content, "urn:ietf:params:netconf:base:1.0")) {
544 ver = 0;
545 } else if (ver < 1 && !strcmp(cpblt->content, "urn:ietf:params:netconf:base:1.1")) {
546 ver = 1;
547 }
548
549 /* store capabilities */
550 if (list) {
551 (*list)[i] = cpblt->content;
552 cpblt->content = NULL;
553 i++;
554 }
555 }
556
557 if (ver == -1) {
558 ERR("Peer does not support compatible NETCONF version.");
559 }
560
561 return ver;
562}
563
564static NC_MSG_TYPE
Michal Vasko086311b2016-01-08 09:53:11 +0100565nc_send_hello(struct nc_session *session)
566{
567 int r, i;
568 const char **cpblts;
569
570 if (session->side == NC_CLIENT) {
571 /* client side hello - send only NETCONF base capabilities */
572 cpblts = malloc(3 * sizeof *cpblts);
573 cpblts[0] = lydict_insert(session->ctx, "urn:ietf:params:netconf:base:1.0", 0);
574 cpblts[1] = lydict_insert(session->ctx, "urn:ietf:params:netconf:base:1.1", 0);
575 cpblts[2] = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100576
577 r = nc_write_msg(session, NC_MSG_HELLO, cpblts, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +0100578 } else {
579 cpblts = create_cpblts(session->ctx);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100580
581 r = nc_write_msg(session, NC_MSG_HELLO, cpblts, &session->id);
Michal Vasko086311b2016-01-08 09:53:11 +0100582 }
583
Michal Vasko086311b2016-01-08 09:53:11 +0100584 for (i = 0; cpblts[i]; ++i) {
585 lydict_remove(session->ctx, cpblts[i]);
586 }
587 free(cpblts);
588
589 if (r) {
590 return NC_MSG_ERROR;
591 } else {
592 return NC_MSG_HELLO;
593 }
594}
595
596static NC_MSG_TYPE
Radek Krejci695d4fa2015-10-22 13:23:54 +0200597nc_recv_hello(struct nc_session *session)
598{
599 struct lyxml_elem *xml = NULL, *node;
600 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
601 int ver = -1;
602 char *str;
603 long long int id;
604 int flag = 0;
605
Michal Vasko05ba9df2016-01-13 14:40:27 +0100606 msgtype = nc_read_msg_poll(session, NC_CLIENT_HELLO_TIMEOUT * 1000, &xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200607
608 switch(msgtype) {
609 case NC_MSG_HELLO:
610 /* parse <hello> data */
611 if (session->side == NC_SERVER) {
612 /* get know NETCONF version */
613 LY_TREE_FOR(xml->child, node) {
614 if (!node->ns || !node->ns->value || strcmp(node->ns->value, NC_NS_BASE)) {
615 continue;
616 } else if (strcmp(node->name, "capabilities")) {
617 ERR("Unexpected <%s> element in client's <hello>.", node->name);
618 goto error;
619 }
620
621 if (flag) {
622 /* multiple capabilities elements */
623 ERR("Invalid <hello> message (multiple <capabilities> elements)");
624 goto error;
625 }
626 flag = 1;
627
Radek Krejci153f5ca2016-01-08 15:38:17 +0100628 if ((ver = parse_cpblts(node, NULL)) < 0) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200629 goto error;
630 }
631 session->version = ver;
632 }
633 } else { /* NC_CLIENT */
634 LY_TREE_FOR(xml->child, node) {
635 if (!node->ns || !node->ns->value || strcmp(node->ns->value, NC_NS_BASE)) {
636 continue;
637 } else if (!strcmp(node->name, "session-id")) {
638 if (!node->content || !strlen(node->content)) {
639 ERR("No value of <session-id> element in server's <hello>");
640 goto error;
641 }
642 str = NULL;
643 id = strtoll(node->content, &str, 10);
644 if (*str || id < 1 || id > UINT32_MAX) {
645 ERR("Invalid value of <session-id> element in server's <hello>");
646 goto error;
647 }
648 session->id = (uint32_t)id;
649 continue;
650 } else if (strcmp(node->name, "capabilities")) {
651 ERR("Unexpected <%s> element in client's <hello>.", node->name);
652 goto error;
653 }
654
655 if (flag) {
656 /* multiple capabilities elements */
657 ERR("Invalid <hello> message (multiple <capabilities> elements)");
658 goto error;
659 }
660 flag = 1;
661
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100662 if ((ver = parse_cpblts(node, &session->cpblts)) < 0) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200663 goto error;
664 }
665 session->version = ver;
666 }
667
668 if (!session->id) {
669 ERR("Missing <session-id> in server's <hello>");
670 goto error;
671 }
672 }
673 break;
674 case NC_MSG_ERROR:
675 /* nothing special, just pass it out */
676 break;
677 default:
678 ERR("Unexpected message received instead of <hello>.");
679 msgtype = NC_MSG_ERROR;
680 }
681
682 /* cleanup */
Michal Vaskoad611702015-12-03 13:41:51 +0100683 lyxml_free(session->ctx, xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200684
685 return msgtype;
686
687error:
688 /* cleanup */
Michal Vaskoad611702015-12-03 13:41:51 +0100689 lyxml_free(session->ctx, xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200690
691 return NC_MSG_ERROR;
Radek Krejci5686ff72015-10-09 13:33:56 +0200692}
693
Michal Vasko80cad7f2015-12-08 14:42:27 +0100694int
Michal Vasko086311b2016-01-08 09:53:11 +0100695nc_handshake(struct nc_session *session)
Michal Vasko80cad7f2015-12-08 14:42:27 +0100696{
Michal Vasko086311b2016-01-08 09:53:11 +0100697 NC_MSG_TYPE type;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100698
Michal Vasko086311b2016-01-08 09:53:11 +0100699 type = nc_send_hello(session);
700 if (type != NC_MSG_HELLO) {
701 return 1;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100702 }
703
Michal Vasko086311b2016-01-08 09:53:11 +0100704 type = nc_recv_hello(session);
705 if (type != NC_MSG_HELLO) {
706 return 1;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100707 }
708
Michal Vasko086311b2016-01-08 09:53:11 +0100709 return 0;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100710}
Michal Vasko086311b2016-01-08 09:53:11 +0100711
712#ifdef ENABLE_SSH
713
714API void
715nc_ssh_init(void)
716{
717 ssh_threads_set_callbacks(ssh_threads_get_pthread());
718 ssh_init();
719 ssh_set_log_level(verbose_level);
720}
721
722API void
723nc_ssh_destroy(void)
724{
725 ssh_finalize();
726}
727
728#endif /* ENABLE_SSH */
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100729
730#ifdef ENABLE_TLS
731
732static pthread_mutex_t *tls_locks;
733
734static void
735tls_thread_locking_func(int mode, int n, const char *file, int line)
736{
737 (void)file;
738 (void)line;
739
740 if (mode & CRYPTO_LOCK) {
741 pthread_mutex_lock(tls_locks + n);
742 } else {
743 pthread_mutex_unlock(tls_locks + n);
744 }
745}
746
747static unsigned long
748tls_thread_id_func(void)
749{
750 return (unsigned long)pthread_self();
751}
752
753API void
754nc_tls_init(void)
755{
756 int i;
757
758 SSL_load_error_strings();
759 SSL_library_init();
760
761 tls_locks = malloc(CRYPTO_num_locks() * sizeof *tls_locks);
762 for (i = 0; i < CRYPTO_num_locks(); ++i) {
763 pthread_mutex_init(tls_locks + i, NULL);
764 }
765
766 CRYPTO_set_id_callback(tls_thread_id_func);
767 CRYPTO_set_locking_callback(tls_thread_locking_func);
768}
769
770API void
771nc_tls_destroy(void)
772{
773 int i;
774
775 CRYPTO_THREADID crypto_tid;
776
777 EVP_cleanup();
778 CRYPTO_cleanup_all_ex_data();
779 ERR_free_strings();
780 sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
781 CRYPTO_THREADID_current(&crypto_tid);
782 ERR_remove_thread_state(&crypto_tid);
783
784 CRYPTO_set_id_callback(NULL);
785 CRYPTO_set_locking_callback(NULL);
786 for (i = 0; i < CRYPTO_num_locks(); ++i) {
787 pthread_mutex_destroy(tls_locks + i);
788 }
789 free(tls_locks);
790}
791
792#endif /* ENABLE_TLS */