blob: 0e8fd547ac6463bf51d92fca6e703bb89bc2c350 [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 Vasko1a38c862016-01-15 15:50:07 +010040#include "libnetconf.h"
Radek Krejci695d4fa2015-10-22 13:23:54 +020041
Michal Vasko086311b2016-01-08 09:53:11 +010042/* in seconds */
43#define NC_CLIENT_HELLO_TIMEOUT 60
Radek Krejci695d4fa2015-10-22 13:23:54 +020044
Michal Vasko05ba9df2016-01-13 14:40:27 +010045/* in milliseconds */
46#define NC_CLOSE_REPLY_TIMEOUT 200
47
Michal Vasko086311b2016-01-08 09:53:11 +010048extern struct nc_server_opts server_opts;
49
Michal Vasko8dadf782016-01-15 10:29:36 +010050API NC_STATUS
51nc_session_get_status(const struct nc_session *session)
52{
53 return session->status;
54}
55
56API uint32_t
57nc_session_get_id(const struct nc_session *session)
58{
59 return session->id;
60}
61
62API NC_TRANSPORT_IMPL
63nc_session_get_ti(const struct nc_session *session)
64{
65 return session->ti_type;
66}
67
68API const char *
69nc_session_get_username(const struct nc_session *session)
70{
71 return session->username;
72}
73
74API const char *
75nc_session_get_host(const struct nc_session *session)
76{
77 return session->host;
78}
79
80API uint16_t
81nc_session_get_port(const struct nc_session *session)
82{
83 return session->port;
84}
85
86API const char **
87nc_session_get_cpblts(const struct nc_session *session)
88{
89 return session->cpblts;
90}
91
92API const char *
93nc_session_cpblt(const struct nc_session *session, const char *capab)
94{
95 int i, len;
96
97 len = strlen(capab);
98 for (i = 0; session->cpblts[i]; ++i) {
99 if (!strncmp(session->cpblts[i], capab, len)) {
100 return session->cpblts[i];
101 }
102 }
103
104 return NULL;
105}
106
Michal Vasko086311b2016-01-08 09:53:11 +0100107NC_MSG_TYPE
108nc_send_msg(struct nc_session *session, struct lyd_node *op)
Radek Krejci695d4fa2015-10-22 13:23:54 +0200109{
Michal Vasko086311b2016-01-08 09:53:11 +0100110 int r;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200111
Michal Vasko086311b2016-01-08 09:53:11 +0100112 if (session->ctx != op->schema->module->ctx) {
113 ERR("RPC \"%s\" was created in different context than that of \"%s\" session %u.",
114 op->schema->name, session->username, session->id);
115 return NC_MSG_ERROR;
Michal Vasko7df39ec2015-12-09 15:26:24 +0100116 }
117
Michal Vasko086311b2016-01-08 09:53:11 +0100118 r = nc_write_msg(session, NC_MSG_RPC, op, NULL);
119
120 if (r) {
121 return NC_MSG_ERROR;
122 }
123
124 return NC_MSG_RPC;
Michal Vasko7df39ec2015-12-09 15:26:24 +0100125}
126
Radek Krejci5686ff72015-10-09 13:33:56 +0200127/*
128 * @return 0 - success
129 * -1 - timeout
130 * >0 - error
131 */
Michal Vasko086311b2016-01-08 09:53:11 +0100132int
Michal Vaskoc111ac52015-12-08 14:36:36 +0100133session_ti_lock(struct nc_session *session, int32_t timeout)
Radek Krejci206fcd62015-10-07 15:42:48 +0200134{
135 int r;
Radek Krejci206fcd62015-10-07 15:42:48 +0200136
137 if (timeout >= 0) {
138 /* limited waiting for lock */
139 do {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200140 r = pthread_mutex_trylock(session->ti_lock);
Radek Krejci206fcd62015-10-07 15:42:48 +0200141 if (r == EBUSY) {
142 /* try later until timeout passes */
Michal Vasko086311b2016-01-08 09:53:11 +0100143 usleep(NC_TIMEOUT_STEP);
144 timeout = timeout - NC_TIMEOUT_STEP;
Radek Krejci206fcd62015-10-07 15:42:48 +0200145 continue;
146 } else if (r) {
147 /* error */
148 ERR("Acquiring session (%u) TI lock failed (%s).", session->id, strerror(r));
Radek Krejci5686ff72015-10-09 13:33:56 +0200149 return r;
Radek Krejci206fcd62015-10-07 15:42:48 +0200150 } else {
151 /* lock acquired */
Radek Krejci5686ff72015-10-09 13:33:56 +0200152 return 0;
Radek Krejci206fcd62015-10-07 15:42:48 +0200153 }
Michal Vasko428087d2016-01-14 16:04:28 +0100154 } while (timeout > 0);
Radek Krejci206fcd62015-10-07 15:42:48 +0200155
Radek Krejci5686ff72015-10-09 13:33:56 +0200156 /* timeout has passed */
157 return -1;
Radek Krejci206fcd62015-10-07 15:42:48 +0200158 } else {
159 /* infinite waiting for lock */
Radek Krejci695d4fa2015-10-22 13:23:54 +0200160 return pthread_mutex_lock(session->ti_lock);
Radek Krejci5686ff72015-10-09 13:33:56 +0200161 }
162}
163
Michal Vasko086311b2016-01-08 09:53:11 +0100164int
Radek Krejci5686ff72015-10-09 13:33:56 +0200165session_ti_unlock(struct nc_session *session)
166{
Radek Krejci695d4fa2015-10-22 13:23:54 +0200167 return pthread_mutex_unlock(session->ti_lock);
168}
169
Radek Krejci695d4fa2015-10-22 13:23:54 +0200170API void
171nc_session_free(struct nc_session *session)
172{
173 int r, i;
Michal Vasko428087d2016-01-14 16:04:28 +0100174 int connected; /* flag to indicate whether the transport socket is still connected */
Radek Krejci695d4fa2015-10-22 13:23:54 +0200175 int multisession = 0; /* flag for more NETCONF session on a single SSH session */
176 struct nc_session *siter;
Michal Vaskoad611702015-12-03 13:41:51 +0100177 struct nc_msg_cont *contiter;
Michal Vaskoadd4c792015-10-26 15:36:58 +0100178 struct lyxml_elem *rpl, *child;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200179 struct lyd_node *close_rpc;
Michal Vaskoad611702015-12-03 13:41:51 +0100180 const struct lys_module *ietfnc;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200181 void *p;
182
Michal Vasko428087d2016-01-14 16:04:28 +0100183 if (!session || (session->status == NC_STATUS_CLOSING)) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200184 return;
185 }
186
187 /* mark session for closing */
Michal Vaskoadd4c792015-10-26 15:36:58 +0100188 if (session->ti_lock) {
189 do {
190 r = session_ti_lock(session, 0);
191 } while (r < 0);
192 if (r) {
193 return;
194 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200195 }
196
197 /* stop notifications loop if any */
198 if (session->notif) {
199 pthread_cancel(*session->notif);
200 pthread_join(*session->notif, NULL);
201 }
202
Michal Vasko428087d2016-01-14 16:04:28 +0100203 if ((session->side == NC_CLIENT) && (session->status == NC_STATUS_RUNNING)) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200204 /* cleanup message queues */
205 /* notifications */
Michal Vaskoad611702015-12-03 13:41:51 +0100206 for (contiter = session->notifs; contiter; ) {
207 lyxml_free(session->ctx, contiter->msg);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200208
Michal Vaskoad611702015-12-03 13:41:51 +0100209 p = contiter;
210 contiter = contiter->next;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200211 free(p);
212 }
213
214 /* rpc replies */
Michal Vaskoad611702015-12-03 13:41:51 +0100215 for (contiter = session->replies; contiter; ) {
216 lyxml_free(session->ctx, contiter->msg);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200217
Michal Vaskoad611702015-12-03 13:41:51 +0100218 p = contiter;
219 contiter = contiter->next;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200220 free(p);
221 }
222
223 /* send closing info to the other side */
224 ietfnc = ly_ctx_get_module(session->ctx, "ietf-netconf", NULL);
225 if (!ietfnc) {
Michal Vasko428087d2016-01-14 16:04:28 +0100226 WRN("Session %u: missing ietf-netconf schema in context, unable to send <close-session>.", session->id);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200227 } else {
228 close_rpc = lyd_new(NULL, ietfnc, "close-session");
Michal Vaskoad611702015-12-03 13:41:51 +0100229 nc_send_msg(session, close_rpc);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200230 lyd_free(close_rpc);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100231 switch (nc_read_msg_poll(session, NC_CLOSE_REPLY_TIMEOUT, &rpl)) {
Michal Vaskofad6e912015-10-26 15:37:22 +0100232 case NC_MSG_REPLY:
233 LY_TREE_FOR(rpl->child, child) {
234 if (!strcmp(child->name, "ok") && child->ns && !strcmp(child->ns->value, NC_NS_BASE)) {
235 break;
236 }
237 }
238 if (!child) {
Michal Vasko428087d2016-01-14 16:04:28 +0100239 WRN("Session %u: the reply to <close-session> was not <ok> as expected.", session->id);
Michal Vaskofad6e912015-10-26 15:37:22 +0100240 }
Michal Vaskoad611702015-12-03 13:41:51 +0100241 lyxml_free(session->ctx, rpl);
Michal Vaskofad6e912015-10-26 15:37:22 +0100242 break;
243 case NC_MSG_WOULDBLOCK:
Michal Vasko428087d2016-01-14 16:04:28 +0100244 WRN("Session %u: timeout for receiving a reply to <close-session> elapsed.", session->id);
Michal Vaskofad6e912015-10-26 15:37:22 +0100245 break;
246 case NC_MSG_ERROR:
Michal Vasko428087d2016-01-14 16:04:28 +0100247 ERR("%s: session %u: failed to receive a reply to <close-session>.", __func__, session->id);
Michal Vaskofad6e912015-10-26 15:37:22 +0100248 break;
249 default:
250 /* cannot happen */
251 break;
252 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200253 }
254
255 /* list of server's capabilities */
256 if (session->cpblts) {
257 for (i = 0; session->cpblts[i]; i++) {
258 lydict_remove(session->ctx, session->cpblts[i]);
259 }
260 free(session->cpblts);
261 }
262 }
263
264 session->status = NC_STATUS_CLOSING;
Michal Vasko428087d2016-01-14 16:04:28 +0100265 connected = nc_session_is_connected(session);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200266
267 /* transport implementation cleanup */
268 switch (session->ti_type) {
269 case NC_TI_FD:
270 /* nothing needed - file descriptors were provided by caller,
271 * so it is up to the caller to close them correctly
272 * TODO use callbacks
273 */
274 break;
275
Michal Vaskofb2fb762015-10-27 11:44:32 +0100276#ifdef ENABLE_SSH
Radek Krejci695d4fa2015-10-22 13:23:54 +0200277 case NC_TI_LIBSSH:
Michal Vasko428087d2016-01-14 16:04:28 +0100278 if (connected) {
279 ssh_channel_free(session->ti.libssh.channel);
280 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200281 /* There can be multiple NETCONF sessions on the same SSH session (NETCONF session maps to
282 * SSH channel). So destroy the SSH session only if there is no other NETCONF session using
283 * it.
284 */
285 if (!session->ti.libssh.next) {
Michal Vasko428087d2016-01-14 16:04:28 +0100286 if (connected) {
287 ssh_disconnect(session->ti.libssh.session);
288 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200289 ssh_free(session->ti.libssh.session);
290 } else {
291 /* multiple NETCONF sessions on a single SSH session */
292 multisession = 1;
293 /* remove the session from the list */
294 for (siter = session->ti.libssh.next; siter->ti.libssh.next != session; siter = siter->ti.libssh.next);
Michal Vaskoaec4f212015-10-26 15:37:45 +0100295 if (session->ti.libssh.next == siter) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200296 /* there will be only one session */
297 siter->ti.libssh.next = NULL;
298 } else {
299 /* there are still multiple sessions, keep the ring list */
300 siter->ti.libssh.next = session->ti.libssh.next;
301 }
302 }
303 break;
304#endif
305
306#ifdef ENABLE_TLS
307 case NC_TI_OPENSSL:
Michal Vasko428087d2016-01-14 16:04:28 +0100308 if (connected) {
309 SSL_shutdown(session->ti.tls);
310 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200311 SSL_free(session->ti.tls);
Michal Vasko06e22432016-01-15 10:30:06 +0100312
313 X509_free(session->tls_cert);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200314 break;
315#endif
Michal Vasko428087d2016-01-14 16:04:28 +0100316 case NC_TI_NONE:
317 ERRINT;
318 break;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200319 }
Michal Vasko428087d2016-01-14 16:04:28 +0100320
Radek Krejciac6d3472015-10-22 15:47:18 +0200321 lydict_remove(session->ctx, session->username);
322 lydict_remove(session->ctx, session->host);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200323
324 /* final cleanup */
Michal Vaskoadd4c792015-10-26 15:36:58 +0100325 if (session->ti_lock) {
326 if (multisession) {
327 session_ti_unlock(session);
328 } else {
329 pthread_mutex_destroy(session->ti_lock);
330 free(session->ti_lock);
331 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200332 }
333
334 if (!(session->flags & NC_SESSION_SHAREDCTX)) {
335 ly_ctx_destroy(session->ctx);
336 }
337
338 free(session);
339}
340
Michal Vasko086311b2016-01-08 09:53:11 +0100341static void
342add_cpblt(struct ly_ctx *ctx, const char *capab, const char ***cpblts, int *size, int *count)
343{
344 if (*count == *size) {
345 *size += 5;
346 *cpblts = realloc(*cpblts, *size * sizeof **cpblts);
347 }
348
349 if (capab) {
350 (*cpblts)[*count] = lydict_insert(ctx, capab, 0);
351 } else {
352 (*cpblts)[*count] = NULL;
353 }
354 ++(*count);
355}
356
357static const char **
358create_cpblts(struct ly_ctx *ctx)
359{
360 struct lyd_node *child, *child2, *yanglib;
361 struct lyd_node_leaf_list **features = NULL, *ns = NULL, *rev = NULL, *name = NULL;
362 const char **cpblts;
363 const struct lys_module *mod;
364 int size = 10, count, feat_count = 0, i;
365 char str[512];
366
367 yanglib = ly_ctx_info(ctx);
368 if (!yanglib) {
369 return NULL;
370 }
371
372 cpblts = malloc(size * sizeof *cpblts);
373 cpblts[0] = lydict_insert(ctx, "urn:ietf:params:netconf:base:1.0", 0);
374 cpblts[1] = lydict_insert(ctx, "urn:ietf:params:netconf:base:1.1", 0);
375 count = 2;
376
377 /* capabilities */
378
379 mod = ly_ctx_get_module(ctx, "ietf-netconf", NULL);
380 if (mod) {
381 if (lys_features_state(mod, "writable-running") == 1) {
382 add_cpblt(ctx, "urn:ietf:params:netconf:writable-running:1.0", &cpblts, &size, &count);
383 }
384 if (lys_features_state(mod, "candidate") == 1) {
385 add_cpblt(ctx, "urn:ietf:params:netconf:candidate:1.0", &cpblts, &size, &count);
386 if (lys_features_state(mod, "confirmed-commit") == 1) {
387 add_cpblt(ctx, "urn:ietf:params:netconf:confirmed-commit:1.1", &cpblts, &size, &count);
388 }
389 }
390 if (lys_features_state(mod, "rollback-on-error") == 1) {
391 add_cpblt(ctx, "urn:ietf:params:netconf:rollback-on-error:1.0", &cpblts, &size, &count);
392 }
393 if (lys_features_state(mod, "validate") == 1) {
394 add_cpblt(ctx, "urn:ietf:params:netconf:validate:1.1", &cpblts, &size, &count);
395 }
396 if (lys_features_state(mod, "startup") == 1) {
397 add_cpblt(ctx, "urn:ietf:params:netconf:startup:1.0", &cpblts, &size, &count);
398 }
399 if (lys_features_state(mod, "url") == 1) {
400 add_cpblt(ctx, "urn:ietf:params:netconf:url:1.0", &cpblts, &size, &count);
401 }
402 if (lys_features_state(mod, "xpath") == 1) {
403 add_cpblt(ctx, "urn:ietf:params:netconf:xpath:1.0", &cpblts, &size, &count);
404 }
405 }
406
407 mod = ly_ctx_get_module(ctx, "ietf-netconf-with-defaults", NULL);
408 if (mod) {
409 if (!server_opts.wd_basic_mode) {
410 VRB("with-defaults capability will not be advertised even though \"ietf-netconf-with-defaults\" model is present, unknown basic-mode.");
411 } else {
412 strcpy(str, "urn:ietf:params:netconf:with-defaults:1.0");
413 switch (server_opts.wd_basic_mode) {
414 case NC_WD_ALL:
415 strcat(str, "?basic-mode=report-all");
416 break;
417 case NC_WD_TRIM:
418 strcat(str, "?basic-mode=trim");
419 break;
420 case NC_WD_EXPLICIT:
421 strcat(str, "?basic-mode=explicit");
422 break;
423 default:
Michal Vasko9e036d52016-01-08 10:49:26 +0100424 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100425 break;
426 }
427
428 if (server_opts.wd_also_supported) {
Radek Krejcif9b28322016-01-08 14:56:43 +0100429 strcat(str, "&amp;also-supported=");
Michal Vasko086311b2016-01-08 09:53:11 +0100430 if (server_opts.wd_also_supported & NC_WD_ALL) {
431 strcat(str, "report-all,");
432 }
433 if (server_opts.wd_also_supported & NC_WD_ALL_TAG) {
434 strcat(str, "report-all-tagged,");
435 }
436 if (server_opts.wd_also_supported & NC_WD_TRIM) {
437 strcat(str, "trim,");
438 }
439 if (server_opts.wd_also_supported & NC_WD_EXPLICIT) {
440 strcat(str, "explicit,");
441 }
442 str[strlen(str) - 1] = '\0';
443
444 add_cpblt(ctx, str, &cpblts, &size, &count);
445 }
446 }
447 }
448
Michal Vasko1a38c862016-01-15 15:50:07 +0100449 mod = ly_ctx_get_module(ctx, "nc-notifications", NULL);
Michal Vasko086311b2016-01-08 09:53:11 +0100450 if (mod) {
451 add_cpblt(ctx, "urn:ietf:params:netconf:notification:1.0", &cpblts, &size, &count);
452 if (server_opts.interleave_capab) {
453 add_cpblt(ctx, "urn:ietf:params:netconf:interleave:1.0", &cpblts, &size, &count);
454 }
455 }
456
457 /* models */
458
459 LY_TREE_FOR(yanglib->child, child) {
460 if (!strcmp(child->schema->name, "module")) {
461 LY_TREE_FOR(child->child, child2) {
462 if (!strcmp(child2->schema->name, "namespace")) {
463 ns = (struct lyd_node_leaf_list *)child2;
464 } else if (!strcmp(child2->schema->name, "name")) {
465 name = (struct lyd_node_leaf_list *)child2;
466 } else if (!strcmp(child2->schema->name, "revision")) {
467 rev = (struct lyd_node_leaf_list *)child2;
468 } else if (!strcmp(child2->schema->name, "feature")) {
469 features = realloc(features, feat_count++ * sizeof *features);
470 features[feat_count - 1] = (struct lyd_node_leaf_list *)child2;
471 }
472 }
473
474 if (!ns || !name || !rev) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100475 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100476 continue;
477 }
478
Radek Krejcif9b28322016-01-08 14:56:43 +0100479 sprintf(str, "%s?module=%s&amp;revision=%s", ns->value_str, name->value_str, rev->value_str);
Michal Vasko086311b2016-01-08 09:53:11 +0100480 if (feat_count) {
Radek Krejcif9b28322016-01-08 14:56:43 +0100481 strcat(str, "&amp;features=");
Michal Vasko086311b2016-01-08 09:53:11 +0100482 for (i = 0; i < feat_count; ++i) {
483 if (i) {
484 strcat(str, ",");
485 }
486 strcat(str, features[i]->value_str);
487 }
488 }
489
490 add_cpblt(ctx, str, &cpblts, &size, &count);
491
492 ns = NULL;
493 name = NULL;
494 rev = NULL;
495 free(features);
496 features = NULL;
497 feat_count = 0;
498 }
499 }
500
501 lyd_free(yanglib);
502
503 /* ending NULL capability */
504 add_cpblt(ctx, NULL, &cpblts, &size, &count);
505
506 return cpblts;
507}
508
Radek Krejci695d4fa2015-10-22 13:23:54 +0200509static int
510parse_cpblts(struct lyxml_elem *xml, const char ***list)
511{
512 struct lyxml_elem *cpblt;
513 int ver = -1;
514 int i = 0;
515
516 if (list) {
517 /* get the storage for server's capabilities */
518 LY_TREE_FOR(xml->child, cpblt) {
519 i++;
520 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100521 /* last item remains NULL */
522 *list = calloc(i + 1, sizeof **list);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200523 if (!*list) {
524 ERRMEM;
525 return -1;
526 }
527 i = 0;
528 }
529
530 LY_TREE_FOR(xml->child, cpblt) {
531 if (strcmp(cpblt->name, "capability") && cpblt->ns && cpblt->ns->value &&
532 !strcmp(cpblt->ns->value, NC_NS_BASE)) {
533 ERR("Unexpected <%s> element in client's <hello>.", cpblt->name);
534 return -1;
535 } else if (!cpblt->ns || !cpblt->ns->value || strcmp(cpblt->ns->value, NC_NS_BASE)) {
536 continue;
537 }
538
539 /* detect NETCONF version */
540 if (ver < 0 && !strcmp(cpblt->content, "urn:ietf:params:netconf:base:1.0")) {
541 ver = 0;
542 } else if (ver < 1 && !strcmp(cpblt->content, "urn:ietf:params:netconf:base:1.1")) {
543 ver = 1;
544 }
545
546 /* store capabilities */
547 if (list) {
548 (*list)[i] = cpblt->content;
549 cpblt->content = NULL;
550 i++;
551 }
552 }
553
554 if (ver == -1) {
555 ERR("Peer does not support compatible NETCONF version.");
556 }
557
558 return ver;
559}
560
561static NC_MSG_TYPE
Michal Vasko086311b2016-01-08 09:53:11 +0100562nc_send_hello(struct nc_session *session)
563{
564 int r, i;
565 const char **cpblts;
566
567 if (session->side == NC_CLIENT) {
568 /* client side hello - send only NETCONF base capabilities */
569 cpblts = malloc(3 * sizeof *cpblts);
570 cpblts[0] = lydict_insert(session->ctx, "urn:ietf:params:netconf:base:1.0", 0);
571 cpblts[1] = lydict_insert(session->ctx, "urn:ietf:params:netconf:base:1.1", 0);
572 cpblts[2] = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100573
574 r = nc_write_msg(session, NC_MSG_HELLO, cpblts, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +0100575 } else {
576 cpblts = create_cpblts(session->ctx);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100577
578 r = nc_write_msg(session, NC_MSG_HELLO, cpblts, &session->id);
Michal Vasko086311b2016-01-08 09:53:11 +0100579 }
580
Michal Vasko086311b2016-01-08 09:53:11 +0100581 for (i = 0; cpblts[i]; ++i) {
582 lydict_remove(session->ctx, cpblts[i]);
583 }
584 free(cpblts);
585
586 if (r) {
587 return NC_MSG_ERROR;
588 } else {
589 return NC_MSG_HELLO;
590 }
591}
592
593static NC_MSG_TYPE
Radek Krejci695d4fa2015-10-22 13:23:54 +0200594nc_recv_hello(struct nc_session *session)
595{
596 struct lyxml_elem *xml = NULL, *node;
597 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
598 int ver = -1;
599 char *str;
600 long long int id;
601 int flag = 0;
602
Michal Vasko05ba9df2016-01-13 14:40:27 +0100603 msgtype = nc_read_msg_poll(session, NC_CLIENT_HELLO_TIMEOUT * 1000, &xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200604
605 switch(msgtype) {
606 case NC_MSG_HELLO:
607 /* parse <hello> data */
608 if (session->side == NC_SERVER) {
609 /* get know NETCONF version */
610 LY_TREE_FOR(xml->child, node) {
611 if (!node->ns || !node->ns->value || strcmp(node->ns->value, NC_NS_BASE)) {
612 continue;
613 } else if (strcmp(node->name, "capabilities")) {
614 ERR("Unexpected <%s> element in client's <hello>.", node->name);
615 goto error;
616 }
617
618 if (flag) {
619 /* multiple capabilities elements */
620 ERR("Invalid <hello> message (multiple <capabilities> elements)");
621 goto error;
622 }
623 flag = 1;
624
Radek Krejci153f5ca2016-01-08 15:38:17 +0100625 if ((ver = parse_cpblts(node, NULL)) < 0) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200626 goto error;
627 }
628 session->version = ver;
629 }
630 } else { /* NC_CLIENT */
631 LY_TREE_FOR(xml->child, node) {
632 if (!node->ns || !node->ns->value || strcmp(node->ns->value, NC_NS_BASE)) {
633 continue;
634 } else if (!strcmp(node->name, "session-id")) {
635 if (!node->content || !strlen(node->content)) {
636 ERR("No value of <session-id> element in server's <hello>");
637 goto error;
638 }
639 str = NULL;
640 id = strtoll(node->content, &str, 10);
641 if (*str || id < 1 || id > UINT32_MAX) {
642 ERR("Invalid value of <session-id> element in server's <hello>");
643 goto error;
644 }
645 session->id = (uint32_t)id;
646 continue;
647 } else if (strcmp(node->name, "capabilities")) {
648 ERR("Unexpected <%s> element in client's <hello>.", node->name);
649 goto error;
650 }
651
652 if (flag) {
653 /* multiple capabilities elements */
654 ERR("Invalid <hello> message (multiple <capabilities> elements)");
655 goto error;
656 }
657 flag = 1;
658
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100659 if ((ver = parse_cpblts(node, &session->cpblts)) < 0) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200660 goto error;
661 }
662 session->version = ver;
663 }
664
665 if (!session->id) {
666 ERR("Missing <session-id> in server's <hello>");
667 goto error;
668 }
669 }
670 break;
671 case NC_MSG_ERROR:
672 /* nothing special, just pass it out */
673 break;
674 default:
675 ERR("Unexpected message received instead of <hello>.");
676 msgtype = NC_MSG_ERROR;
677 }
678
679 /* cleanup */
Michal Vaskoad611702015-12-03 13:41:51 +0100680 lyxml_free(session->ctx, xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200681
682 return msgtype;
683
684error:
685 /* cleanup */
Michal Vaskoad611702015-12-03 13:41:51 +0100686 lyxml_free(session->ctx, xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200687
688 return NC_MSG_ERROR;
Radek Krejci5686ff72015-10-09 13:33:56 +0200689}
690
Michal Vasko80cad7f2015-12-08 14:42:27 +0100691int
Michal Vasko086311b2016-01-08 09:53:11 +0100692nc_handshake(struct nc_session *session)
Michal Vasko80cad7f2015-12-08 14:42:27 +0100693{
Michal Vasko086311b2016-01-08 09:53:11 +0100694 NC_MSG_TYPE type;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100695
Michal Vasko086311b2016-01-08 09:53:11 +0100696 type = nc_send_hello(session);
697 if (type != NC_MSG_HELLO) {
698 return 1;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100699 }
700
Michal Vasko086311b2016-01-08 09:53:11 +0100701 type = nc_recv_hello(session);
702 if (type != NC_MSG_HELLO) {
703 return 1;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100704 }
705
Michal Vasko086311b2016-01-08 09:53:11 +0100706 return 0;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100707}
Michal Vasko086311b2016-01-08 09:53:11 +0100708
709#ifdef ENABLE_SSH
710
711API void
712nc_ssh_init(void)
713{
714 ssh_threads_set_callbacks(ssh_threads_get_pthread());
715 ssh_init();
716 ssh_set_log_level(verbose_level);
717}
718
719API void
720nc_ssh_destroy(void)
721{
722 ssh_finalize();
723}
724
725#endif /* ENABLE_SSH */
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100726
727#ifdef ENABLE_TLS
728
729static pthread_mutex_t *tls_locks;
730
731static void
732tls_thread_locking_func(int mode, int n, const char *file, int line)
733{
734 (void)file;
735 (void)line;
736
737 if (mode & CRYPTO_LOCK) {
738 pthread_mutex_lock(tls_locks + n);
739 } else {
740 pthread_mutex_unlock(tls_locks + n);
741 }
742}
743
744static unsigned long
745tls_thread_id_func(void)
746{
747 return (unsigned long)pthread_self();
748}
749
750API void
751nc_tls_init(void)
752{
753 int i;
754
755 SSL_load_error_strings();
756 SSL_library_init();
757
758 tls_locks = malloc(CRYPTO_num_locks() * sizeof *tls_locks);
759 for (i = 0; i < CRYPTO_num_locks(); ++i) {
760 pthread_mutex_init(tls_locks + i, NULL);
761 }
762
763 CRYPTO_set_id_callback(tls_thread_id_func);
764 CRYPTO_set_locking_callback(tls_thread_locking_func);
765}
766
767API void
768nc_tls_destroy(void)
769{
770 int i;
771
772 CRYPTO_THREADID crypto_tid;
773
774 EVP_cleanup();
775 CRYPTO_cleanup_all_ex_data();
776 ERR_free_strings();
777 sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
778 CRYPTO_THREADID_current(&crypto_tid);
779 ERR_remove_thread_state(&crypto_tid);
780
781 CRYPTO_set_id_callback(NULL);
782 CRYPTO_set_locking_callback(NULL);
783 for (i = 0; i < CRYPTO_num_locks(); ++i) {
784 pthread_mutex_destroy(tls_locks + i);
785 }
786 free(tls_locks);
787}
788
789#endif /* ENABLE_TLS */