blob: 6ebebb35fa18db18a89892742cff6826d1ca62de [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
53NC_MSG_TYPE
54nc_send_msg(struct nc_session *session, struct lyd_node *op)
Radek Krejci695d4fa2015-10-22 13:23:54 +020055{
Michal Vasko086311b2016-01-08 09:53:11 +010056 int r;
Radek Krejci695d4fa2015-10-22 13:23:54 +020057
Michal Vasko086311b2016-01-08 09:53:11 +010058 if (session->ctx != op->schema->module->ctx) {
59 ERR("RPC \"%s\" was created in different context than that of \"%s\" session %u.",
60 op->schema->name, session->username, session->id);
61 return NC_MSG_ERROR;
Michal Vasko7df39ec2015-12-09 15:26:24 +010062 }
63
Michal Vasko086311b2016-01-08 09:53:11 +010064 r = nc_write_msg(session, NC_MSG_RPC, op, NULL);
65
66 if (r) {
67 return NC_MSG_ERROR;
68 }
69
70 return NC_MSG_RPC;
Michal Vasko7df39ec2015-12-09 15:26:24 +010071}
72
Radek Krejci5686ff72015-10-09 13:33:56 +020073/*
74 * @return 0 - success
75 * -1 - timeout
76 * >0 - error
77 */
Michal Vasko086311b2016-01-08 09:53:11 +010078int
Michal Vaskoc111ac52015-12-08 14:36:36 +010079session_ti_lock(struct nc_session *session, int32_t timeout)
Radek Krejci206fcd62015-10-07 15:42:48 +020080{
81 int r;
Radek Krejci206fcd62015-10-07 15:42:48 +020082
83 if (timeout >= 0) {
84 /* limited waiting for lock */
85 do {
Radek Krejci695d4fa2015-10-22 13:23:54 +020086 r = pthread_mutex_trylock(session->ti_lock);
Radek Krejci206fcd62015-10-07 15:42:48 +020087 if (r == EBUSY) {
88 /* try later until timeout passes */
Michal Vasko086311b2016-01-08 09:53:11 +010089 usleep(NC_TIMEOUT_STEP);
90 timeout = timeout - NC_TIMEOUT_STEP;
Radek Krejci206fcd62015-10-07 15:42:48 +020091 continue;
92 } else if (r) {
93 /* error */
94 ERR("Acquiring session (%u) TI lock failed (%s).", session->id, strerror(r));
Radek Krejci5686ff72015-10-09 13:33:56 +020095 return r;
Radek Krejci206fcd62015-10-07 15:42:48 +020096 } else {
97 /* lock acquired */
Radek Krejci5686ff72015-10-09 13:33:56 +020098 return 0;
Radek Krejci206fcd62015-10-07 15:42:48 +020099 }
100 } while(timeout > 0);
101
Radek Krejci5686ff72015-10-09 13:33:56 +0200102 /* timeout has passed */
103 return -1;
Radek Krejci206fcd62015-10-07 15:42:48 +0200104 } else {
105 /* infinite waiting for lock */
Radek Krejci695d4fa2015-10-22 13:23:54 +0200106 return pthread_mutex_lock(session->ti_lock);
Radek Krejci5686ff72015-10-09 13:33:56 +0200107 }
108}
109
Michal Vasko086311b2016-01-08 09:53:11 +0100110int
Radek Krejci5686ff72015-10-09 13:33:56 +0200111session_ti_unlock(struct nc_session *session)
112{
Radek Krejci695d4fa2015-10-22 13:23:54 +0200113 return pthread_mutex_unlock(session->ti_lock);
114}
115
Radek Krejci695d4fa2015-10-22 13:23:54 +0200116API void
117nc_session_free(struct nc_session *session)
118{
119 int r, i;
120 int multisession = 0; /* flag for more NETCONF session on a single SSH session */
121 struct nc_session *siter;
Michal Vaskoad611702015-12-03 13:41:51 +0100122 struct nc_msg_cont *contiter;
Michal Vaskoadd4c792015-10-26 15:36:58 +0100123 struct lyxml_elem *rpl, *child;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200124 struct lyd_node *close_rpc;
Michal Vaskoad611702015-12-03 13:41:51 +0100125 const struct lys_module *ietfnc;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200126 void *p;
127
Michal Vaskoadd4c792015-10-26 15:36:58 +0100128 if (!session || session->status == NC_STATUS_CLOSING) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200129 return;
130 }
131
132 /* mark session for closing */
Michal Vaskoadd4c792015-10-26 15:36:58 +0100133 if (session->ti_lock) {
134 do {
135 r = session_ti_lock(session, 0);
136 } while (r < 0);
137 if (r) {
138 return;
139 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200140 }
141
142 /* stop notifications loop if any */
143 if (session->notif) {
144 pthread_cancel(*session->notif);
145 pthread_join(*session->notif, NULL);
146 }
147
148 if (session->side == NC_CLIENT && session->status == NC_STATUS_RUNNING) {
149 /* cleanup message queues */
150 /* notifications */
Michal Vaskoad611702015-12-03 13:41:51 +0100151 for (contiter = session->notifs; contiter; ) {
152 lyxml_free(session->ctx, contiter->msg);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200153
Michal Vaskoad611702015-12-03 13:41:51 +0100154 p = contiter;
155 contiter = contiter->next;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200156 free(p);
157 }
158
159 /* rpc replies */
Michal Vaskoad611702015-12-03 13:41:51 +0100160 for (contiter = session->replies; contiter; ) {
161 lyxml_free(session->ctx, contiter->msg);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200162
Michal Vaskoad611702015-12-03 13:41:51 +0100163 p = contiter;
164 contiter = contiter->next;
Radek Krejci695d4fa2015-10-22 13:23:54 +0200165 free(p);
166 }
167
168 /* send closing info to the other side */
169 ietfnc = ly_ctx_get_module(session->ctx, "ietf-netconf", NULL);
170 if (!ietfnc) {
Michal Vaskoad611702015-12-03 13:41:51 +0100171 WRN("%s: Missing ietf-netconf schema in context (session %u), unable to send <close-session\\>", __func__, session->id);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200172 } else {
173 close_rpc = lyd_new(NULL, ietfnc, "close-session");
Michal Vaskoad611702015-12-03 13:41:51 +0100174 nc_send_msg(session, close_rpc);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200175 lyd_free(close_rpc);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100176 switch (nc_read_msg_poll(session, NC_CLOSE_REPLY_TIMEOUT, &rpl)) {
Michal Vaskofad6e912015-10-26 15:37:22 +0100177 case NC_MSG_REPLY:
178 LY_TREE_FOR(rpl->child, child) {
179 if (!strcmp(child->name, "ok") && child->ns && !strcmp(child->ns->value, NC_NS_BASE)) {
180 break;
181 }
182 }
183 if (!child) {
184 WRN("The reply to <close-session\\> was not <ok\\> as expected.");
185 }
Michal Vaskoad611702015-12-03 13:41:51 +0100186 lyxml_free(session->ctx, rpl);
Michal Vaskofad6e912015-10-26 15:37:22 +0100187 break;
188 case NC_MSG_WOULDBLOCK:
189 WRN("Timeout for receiving a reply to <close-session\\> elapsed.");
190 break;
191 case NC_MSG_ERROR:
192 ERR("Failed to receive a reply to <close-session\\>.");
193 break;
194 default:
195 /* cannot happen */
196 break;
197 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200198 }
199
200 /* list of server's capabilities */
201 if (session->cpblts) {
202 for (i = 0; session->cpblts[i]; i++) {
203 lydict_remove(session->ctx, session->cpblts[i]);
204 }
205 free(session->cpblts);
206 }
207 }
208
209 session->status = NC_STATUS_CLOSING;
210
211 /* transport implementation cleanup */
212 switch (session->ti_type) {
Michal Vasko38a7c6c2015-12-04 12:29:20 +0100213 case NC_TI_NONE:
214 break;
215
Radek Krejci695d4fa2015-10-22 13:23:54 +0200216 case NC_TI_FD:
217 /* nothing needed - file descriptors were provided by caller,
218 * so it is up to the caller to close them correctly
219 * TODO use callbacks
220 */
221 break;
222
Michal Vaskofb2fb762015-10-27 11:44:32 +0100223#ifdef ENABLE_SSH
Radek Krejci695d4fa2015-10-22 13:23:54 +0200224 case NC_TI_LIBSSH:
225 ssh_channel_free(session->ti.libssh.channel);
226 /* There can be multiple NETCONF sessions on the same SSH session (NETCONF session maps to
227 * SSH channel). So destroy the SSH session only if there is no other NETCONF session using
228 * it.
229 */
230 if (!session->ti.libssh.next) {
231 ssh_disconnect(session->ti.libssh.session);
232 ssh_free(session->ti.libssh.session);
233 } else {
234 /* multiple NETCONF sessions on a single SSH session */
235 multisession = 1;
236 /* remove the session from the list */
237 for (siter = session->ti.libssh.next; siter->ti.libssh.next != session; siter = siter->ti.libssh.next);
Michal Vaskoaec4f212015-10-26 15:37:45 +0100238 if (session->ti.libssh.next == siter) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200239 /* there will be only one session */
240 siter->ti.libssh.next = NULL;
241 } else {
242 /* there are still multiple sessions, keep the ring list */
243 siter->ti.libssh.next = session->ti.libssh.next;
244 }
245 }
246 break;
247#endif
248
249#ifdef ENABLE_TLS
250 case NC_TI_OPENSSL:
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100251 X509_free(session->cert);
252
Radek Krejci695d4fa2015-10-22 13:23:54 +0200253 SSL_shutdown(session->ti.tls);
254 SSL_free(session->ti.tls);
255 break;
256#endif
257 }
Radek Krejciac6d3472015-10-22 15:47:18 +0200258 lydict_remove(session->ctx, session->username);
259 lydict_remove(session->ctx, session->host);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200260
261 /* final cleanup */
Michal Vaskoadd4c792015-10-26 15:36:58 +0100262 if (session->ti_lock) {
263 if (multisession) {
264 session_ti_unlock(session);
265 } else {
266 pthread_mutex_destroy(session->ti_lock);
267 free(session->ti_lock);
268 }
Radek Krejci695d4fa2015-10-22 13:23:54 +0200269 }
270
271 if (!(session->flags & NC_SESSION_SHAREDCTX)) {
272 ly_ctx_destroy(session->ctx);
273 }
274
275 free(session);
276}
277
Michal Vasko086311b2016-01-08 09:53:11 +0100278static void
279add_cpblt(struct ly_ctx *ctx, const char *capab, const char ***cpblts, int *size, int *count)
280{
281 if (*count == *size) {
282 *size += 5;
283 *cpblts = realloc(*cpblts, *size * sizeof **cpblts);
284 }
285
286 if (capab) {
287 (*cpblts)[*count] = lydict_insert(ctx, capab, 0);
288 } else {
289 (*cpblts)[*count] = NULL;
290 }
291 ++(*count);
292}
293
294static const char **
295create_cpblts(struct ly_ctx *ctx)
296{
297 struct lyd_node *child, *child2, *yanglib;
298 struct lyd_node_leaf_list **features = NULL, *ns = NULL, *rev = NULL, *name = NULL;
299 const char **cpblts;
300 const struct lys_module *mod;
301 int size = 10, count, feat_count = 0, i;
302 char str[512];
303
304 yanglib = ly_ctx_info(ctx);
305 if (!yanglib) {
306 return NULL;
307 }
308
309 cpblts = malloc(size * sizeof *cpblts);
310 cpblts[0] = lydict_insert(ctx, "urn:ietf:params:netconf:base:1.0", 0);
311 cpblts[1] = lydict_insert(ctx, "urn:ietf:params:netconf:base:1.1", 0);
312 count = 2;
313
314 /* capabilities */
315
316 mod = ly_ctx_get_module(ctx, "ietf-netconf", NULL);
317 if (mod) {
318 if (lys_features_state(mod, "writable-running") == 1) {
319 add_cpblt(ctx, "urn:ietf:params:netconf:writable-running:1.0", &cpblts, &size, &count);
320 }
321 if (lys_features_state(mod, "candidate") == 1) {
322 add_cpblt(ctx, "urn:ietf:params:netconf:candidate:1.0", &cpblts, &size, &count);
323 if (lys_features_state(mod, "confirmed-commit") == 1) {
324 add_cpblt(ctx, "urn:ietf:params:netconf:confirmed-commit:1.1", &cpblts, &size, &count);
325 }
326 }
327 if (lys_features_state(mod, "rollback-on-error") == 1) {
328 add_cpblt(ctx, "urn:ietf:params:netconf:rollback-on-error:1.0", &cpblts, &size, &count);
329 }
330 if (lys_features_state(mod, "validate") == 1) {
331 add_cpblt(ctx, "urn:ietf:params:netconf:validate:1.1", &cpblts, &size, &count);
332 }
333 if (lys_features_state(mod, "startup") == 1) {
334 add_cpblt(ctx, "urn:ietf:params:netconf:startup:1.0", &cpblts, &size, &count);
335 }
336 if (lys_features_state(mod, "url") == 1) {
337 add_cpblt(ctx, "urn:ietf:params:netconf:url:1.0", &cpblts, &size, &count);
338 }
339 if (lys_features_state(mod, "xpath") == 1) {
340 add_cpblt(ctx, "urn:ietf:params:netconf:xpath:1.0", &cpblts, &size, &count);
341 }
342 }
343
344 mod = ly_ctx_get_module(ctx, "ietf-netconf-with-defaults", NULL);
345 if (mod) {
346 if (!server_opts.wd_basic_mode) {
347 VRB("with-defaults capability will not be advertised even though \"ietf-netconf-with-defaults\" model is present, unknown basic-mode.");
348 } else {
349 strcpy(str, "urn:ietf:params:netconf:with-defaults:1.0");
350 switch (server_opts.wd_basic_mode) {
351 case NC_WD_ALL:
352 strcat(str, "?basic-mode=report-all");
353 break;
354 case NC_WD_TRIM:
355 strcat(str, "?basic-mode=trim");
356 break;
357 case NC_WD_EXPLICIT:
358 strcat(str, "?basic-mode=explicit");
359 break;
360 default:
Michal Vasko9e036d52016-01-08 10:49:26 +0100361 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100362 break;
363 }
364
365 if (server_opts.wd_also_supported) {
Radek Krejcif9b28322016-01-08 14:56:43 +0100366 strcat(str, "&amp;also-supported=");
Michal Vasko086311b2016-01-08 09:53:11 +0100367 if (server_opts.wd_also_supported & NC_WD_ALL) {
368 strcat(str, "report-all,");
369 }
370 if (server_opts.wd_also_supported & NC_WD_ALL_TAG) {
371 strcat(str, "report-all-tagged,");
372 }
373 if (server_opts.wd_also_supported & NC_WD_TRIM) {
374 strcat(str, "trim,");
375 }
376 if (server_opts.wd_also_supported & NC_WD_EXPLICIT) {
377 strcat(str, "explicit,");
378 }
379 str[strlen(str) - 1] = '\0';
380
381 add_cpblt(ctx, str, &cpblts, &size, &count);
382 }
383 }
384 }
385
386 mod = ly_ctx_get_module(ctx, "ietf-netconf-notifications", NULL);
387 if (mod) {
388 add_cpblt(ctx, "urn:ietf:params:netconf:notification:1.0", &cpblts, &size, &count);
389 if (server_opts.interleave_capab) {
390 add_cpblt(ctx, "urn:ietf:params:netconf:interleave:1.0", &cpblts, &size, &count);
391 }
392 }
393
394 /* models */
395
396 LY_TREE_FOR(yanglib->child, child) {
397 if (!strcmp(child->schema->name, "module")) {
398 LY_TREE_FOR(child->child, child2) {
399 if (!strcmp(child2->schema->name, "namespace")) {
400 ns = (struct lyd_node_leaf_list *)child2;
401 } else if (!strcmp(child2->schema->name, "name")) {
402 name = (struct lyd_node_leaf_list *)child2;
403 } else if (!strcmp(child2->schema->name, "revision")) {
404 rev = (struct lyd_node_leaf_list *)child2;
405 } else if (!strcmp(child2->schema->name, "feature")) {
406 features = realloc(features, feat_count++ * sizeof *features);
407 features[feat_count - 1] = (struct lyd_node_leaf_list *)child2;
408 }
409 }
410
411 if (!ns || !name || !rev) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100412 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100413 continue;
414 }
415
Radek Krejcif9b28322016-01-08 14:56:43 +0100416 sprintf(str, "%s?module=%s&amp;revision=%s", ns->value_str, name->value_str, rev->value_str);
Michal Vasko086311b2016-01-08 09:53:11 +0100417 if (feat_count) {
Radek Krejcif9b28322016-01-08 14:56:43 +0100418 strcat(str, "&amp;features=");
Michal Vasko086311b2016-01-08 09:53:11 +0100419 for (i = 0; i < feat_count; ++i) {
420 if (i) {
421 strcat(str, ",");
422 }
423 strcat(str, features[i]->value_str);
424 }
425 }
426
427 add_cpblt(ctx, str, &cpblts, &size, &count);
428
429 ns = NULL;
430 name = NULL;
431 rev = NULL;
432 free(features);
433 features = NULL;
434 feat_count = 0;
435 }
436 }
437
438 lyd_free(yanglib);
439
440 /* ending NULL capability */
441 add_cpblt(ctx, NULL, &cpblts, &size, &count);
442
443 return cpblts;
444}
445
Radek Krejci695d4fa2015-10-22 13:23:54 +0200446static int
447parse_cpblts(struct lyxml_elem *xml, const char ***list)
448{
449 struct lyxml_elem *cpblt;
450 int ver = -1;
451 int i = 0;
452
453 if (list) {
454 /* get the storage for server's capabilities */
455 LY_TREE_FOR(xml->child, cpblt) {
456 i++;
457 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100458 /* last item remains NULL */
459 *list = calloc(i + 1, sizeof **list);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200460 if (!*list) {
461 ERRMEM;
462 return -1;
463 }
464 i = 0;
465 }
466
467 LY_TREE_FOR(xml->child, cpblt) {
468 if (strcmp(cpblt->name, "capability") && cpblt->ns && cpblt->ns->value &&
469 !strcmp(cpblt->ns->value, NC_NS_BASE)) {
470 ERR("Unexpected <%s> element in client's <hello>.", cpblt->name);
471 return -1;
472 } else if (!cpblt->ns || !cpblt->ns->value || strcmp(cpblt->ns->value, NC_NS_BASE)) {
473 continue;
474 }
475
476 /* detect NETCONF version */
477 if (ver < 0 && !strcmp(cpblt->content, "urn:ietf:params:netconf:base:1.0")) {
478 ver = 0;
479 } else if (ver < 1 && !strcmp(cpblt->content, "urn:ietf:params:netconf:base:1.1")) {
480 ver = 1;
481 }
482
483 /* store capabilities */
484 if (list) {
485 (*list)[i] = cpblt->content;
486 cpblt->content = NULL;
487 i++;
488 }
489 }
490
491 if (ver == -1) {
492 ERR("Peer does not support compatible NETCONF version.");
493 }
494
495 return ver;
496}
497
498static NC_MSG_TYPE
Michal Vasko086311b2016-01-08 09:53:11 +0100499nc_send_hello(struct nc_session *session)
500{
501 int r, i;
502 const char **cpblts;
503
504 if (session->side == NC_CLIENT) {
505 /* client side hello - send only NETCONF base capabilities */
506 cpblts = malloc(3 * sizeof *cpblts);
507 cpblts[0] = lydict_insert(session->ctx, "urn:ietf:params:netconf:base:1.0", 0);
508 cpblts[1] = lydict_insert(session->ctx, "urn:ietf:params:netconf:base:1.1", 0);
509 cpblts[2] = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100510
511 r = nc_write_msg(session, NC_MSG_HELLO, cpblts, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +0100512 } else {
513 cpblts = create_cpblts(session->ctx);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100514
515 r = nc_write_msg(session, NC_MSG_HELLO, cpblts, &session->id);
Michal Vasko086311b2016-01-08 09:53:11 +0100516 }
517
Michal Vasko086311b2016-01-08 09:53:11 +0100518 for (i = 0; cpblts[i]; ++i) {
519 lydict_remove(session->ctx, cpblts[i]);
520 }
521 free(cpblts);
522
523 if (r) {
524 return NC_MSG_ERROR;
525 } else {
526 return NC_MSG_HELLO;
527 }
528}
529
530static NC_MSG_TYPE
Radek Krejci695d4fa2015-10-22 13:23:54 +0200531nc_recv_hello(struct nc_session *session)
532{
533 struct lyxml_elem *xml = NULL, *node;
534 NC_MSG_TYPE msgtype = 0; /* NC_MSG_ERROR */
535 int ver = -1;
536 char *str;
537 long long int id;
538 int flag = 0;
539
Michal Vasko05ba9df2016-01-13 14:40:27 +0100540 msgtype = nc_read_msg_poll(session, NC_CLIENT_HELLO_TIMEOUT * 1000, &xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200541
542 switch(msgtype) {
543 case NC_MSG_HELLO:
544 /* parse <hello> data */
545 if (session->side == NC_SERVER) {
546 /* get know NETCONF version */
547 LY_TREE_FOR(xml->child, node) {
548 if (!node->ns || !node->ns->value || strcmp(node->ns->value, NC_NS_BASE)) {
549 continue;
550 } else if (strcmp(node->name, "capabilities")) {
551 ERR("Unexpected <%s> element in client's <hello>.", node->name);
552 goto error;
553 }
554
555 if (flag) {
556 /* multiple capabilities elements */
557 ERR("Invalid <hello> message (multiple <capabilities> elements)");
558 goto error;
559 }
560 flag = 1;
561
Radek Krejci153f5ca2016-01-08 15:38:17 +0100562 if ((ver = parse_cpblts(node, NULL)) < 0) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200563 goto error;
564 }
565 session->version = ver;
566 }
567 } else { /* NC_CLIENT */
568 LY_TREE_FOR(xml->child, node) {
569 if (!node->ns || !node->ns->value || strcmp(node->ns->value, NC_NS_BASE)) {
570 continue;
571 } else if (!strcmp(node->name, "session-id")) {
572 if (!node->content || !strlen(node->content)) {
573 ERR("No value of <session-id> element in server's <hello>");
574 goto error;
575 }
576 str = NULL;
577 id = strtoll(node->content, &str, 10);
578 if (*str || id < 1 || id > UINT32_MAX) {
579 ERR("Invalid value of <session-id> element in server's <hello>");
580 goto error;
581 }
582 session->id = (uint32_t)id;
583 continue;
584 } else if (strcmp(node->name, "capabilities")) {
585 ERR("Unexpected <%s> element in client's <hello>.", node->name);
586 goto error;
587 }
588
589 if (flag) {
590 /* multiple capabilities elements */
591 ERR("Invalid <hello> message (multiple <capabilities> elements)");
592 goto error;
593 }
594 flag = 1;
595
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100596 if ((ver = parse_cpblts(node, &session->cpblts)) < 0) {
Radek Krejci695d4fa2015-10-22 13:23:54 +0200597 goto error;
598 }
599 session->version = ver;
600 }
601
602 if (!session->id) {
603 ERR("Missing <session-id> in server's <hello>");
604 goto error;
605 }
606 }
607 break;
608 case NC_MSG_ERROR:
609 /* nothing special, just pass it out */
610 break;
611 default:
612 ERR("Unexpected message received instead of <hello>.");
613 msgtype = NC_MSG_ERROR;
614 }
615
616 /* cleanup */
Michal Vaskoad611702015-12-03 13:41:51 +0100617 lyxml_free(session->ctx, xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200618
619 return msgtype;
620
621error:
622 /* cleanup */
Michal Vaskoad611702015-12-03 13:41:51 +0100623 lyxml_free(session->ctx, xml);
Radek Krejci695d4fa2015-10-22 13:23:54 +0200624
625 return NC_MSG_ERROR;
Radek Krejci5686ff72015-10-09 13:33:56 +0200626}
627
Michal Vasko80cad7f2015-12-08 14:42:27 +0100628int
Michal Vasko086311b2016-01-08 09:53:11 +0100629nc_handshake(struct nc_session *session)
Michal Vasko80cad7f2015-12-08 14:42:27 +0100630{
Michal Vasko086311b2016-01-08 09:53:11 +0100631 NC_MSG_TYPE type;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100632
Michal Vasko086311b2016-01-08 09:53:11 +0100633 type = nc_send_hello(session);
634 if (type != NC_MSG_HELLO) {
635 return 1;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100636 }
637
Michal Vasko086311b2016-01-08 09:53:11 +0100638 type = nc_recv_hello(session);
639 if (type != NC_MSG_HELLO) {
640 return 1;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100641 }
642
Michal Vasko086311b2016-01-08 09:53:11 +0100643 return 0;
Michal Vasko80cad7f2015-12-08 14:42:27 +0100644}
Michal Vasko086311b2016-01-08 09:53:11 +0100645
646#ifdef ENABLE_SSH
647
648API void
649nc_ssh_init(void)
650{
651 ssh_threads_set_callbacks(ssh_threads_get_pthread());
652 ssh_init();
653 ssh_set_log_level(verbose_level);
654}
655
656API void
657nc_ssh_destroy(void)
658{
659 ssh_finalize();
660}
661
662#endif /* ENABLE_SSH */
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100663
664#ifdef ENABLE_TLS
665
666static pthread_mutex_t *tls_locks;
667
668static void
669tls_thread_locking_func(int mode, int n, const char *file, int line)
670{
671 (void)file;
672 (void)line;
673
674 if (mode & CRYPTO_LOCK) {
675 pthread_mutex_lock(tls_locks + n);
676 } else {
677 pthread_mutex_unlock(tls_locks + n);
678 }
679}
680
681static unsigned long
682tls_thread_id_func(void)
683{
684 return (unsigned long)pthread_self();
685}
686
687API void
688nc_tls_init(void)
689{
690 int i;
691
692 SSL_load_error_strings();
693 SSL_library_init();
694
695 tls_locks = malloc(CRYPTO_num_locks() * sizeof *tls_locks);
696 for (i = 0; i < CRYPTO_num_locks(); ++i) {
697 pthread_mutex_init(tls_locks + i, NULL);
698 }
699
700 CRYPTO_set_id_callback(tls_thread_id_func);
701 CRYPTO_set_locking_callback(tls_thread_locking_func);
702}
703
704API void
705nc_tls_destroy(void)
706{
707 int i;
708
709 CRYPTO_THREADID crypto_tid;
710
711 EVP_cleanup();
712 CRYPTO_cleanup_all_ex_data();
713 ERR_free_strings();
714 sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
715 CRYPTO_THREADID_current(&crypto_tid);
716 ERR_remove_thread_state(&crypto_tid);
717
718 CRYPTO_set_id_callback(NULL);
719 CRYPTO_set_locking_callback(NULL);
720 for (i = 0; i < CRYPTO_num_locks(); ++i) {
721 pthread_mutex_destroy(tls_locks + i);
722 }
723 free(tls_locks);
724}
725
726#endif /* ENABLE_TLS */