blob: 4b4f5739c441b2b915583ed8c999320881c3788c [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
2 * \file session_server.c
3 * \author Michal Vasko <mvasko@cesnet.cz>
4 * \brief libnetconf2 server session manipulation functions
5 *
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
23#include <stdint.h>
24#include <stdlib.h>
25#include <errno.h>
26#include <string.h>
27#include <poll.h>
28#include <sys/types.h>
29#include <sys/socket.h>
30#include <netinet/in.h>
31#include <arpa/inet.h>
32#include <unistd.h>
33
Michal Vasko1a38c862016-01-15 15:50:07 +010034#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010035#include "session_server.h"
36
37struct nc_server_opts server_opts;
Michal Vasko05ba9df2016-01-13 14:40:27 +010038uint32_t session_id = 1;
Michal Vasko086311b2016-01-08 09:53:11 +010039
Michal Vasko1a38c862016-01-15 15:50:07 +010040API void
41nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
42{
43 if (!session || !reason) {
44 ERRARG;
45 return;
46 }
47
48 session->term_reason = reason;
49}
50
Michal Vasko086311b2016-01-08 09:53:11 +010051int
52nc_sock_listen(const char *address, uint32_t port)
53{
54 const int optVal = 1;
55 const socklen_t optLen = sizeof(optVal);
56 int is_ipv4, sock;
57 struct sockaddr_storage saddr;
58
59 struct sockaddr_in *saddr4;
60 struct sockaddr_in6 *saddr6;
61
62
63 if (!strchr(address, ':')) {
64 is_ipv4 = 1;
65 } else {
66 is_ipv4 = 0;
67 }
68
69 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
70 if (sock == -1) {
71 ERR("%s: could not create socket (%s)", __func__, strerror(errno));
72 goto fail;
73 }
74
75 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&optVal, optLen)) {
76 ERR("%s: could not set socket SO_REUSEADDR option (%s)", __func__, strerror(errno));
77 goto fail;
78 }
79
80 bzero(&saddr, sizeof(struct sockaddr_storage));
81 if (is_ipv4) {
82 saddr4 = (struct sockaddr_in *)&saddr;
83
84 saddr4->sin_family = AF_INET;
85 saddr4->sin_port = htons(port);
86
87 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
88 ERR("%s: failed to convert IPv4 address \"%s\"", __func__, address);
89 goto fail;
90 }
91
92 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
93 ERR("%s: could not bind \"%s\" port %d (%s)", __func__, address, port, strerror(errno));
94 goto fail;
95 }
96
97 } else {
98 saddr6 = (struct sockaddr_in6 *)&saddr;
99
100 saddr6->sin6_family = AF_INET6;
101 saddr6->sin6_port = htons(port);
102
103 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
104 ERR("%s: failed to convert IPv6 address \"%s\"", __func__, address);
105 goto fail;
106 }
107
108 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
109 ERR("%s: could not bind \"%s\" port %d (%s)", __func__, address, port, strerror(errno));
110 goto fail;
111 }
112 }
113
Michal Vaskofb89d772016-01-08 12:25:35 +0100114 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vasko086311b2016-01-08 09:53:11 +0100115 ERR("%s: unable to start listening on \"%s\" port %d (%s)", __func__, address, port, strerror(errno));
116 goto fail;
117 }
118
119 return sock;
120
121fail:
122 if (sock > -1) {
123 close(sock);
124 }
125
126 return -1;
127}
128
129int
Michal Vasko9e036d52016-01-08 10:49:26 +0100130nc_sock_accept(struct nc_bind *binds, uint16_t bind_count, int timeout, NC_TRANSPORT_IMPL *ti, char **host, uint16_t *port)
Michal Vasko086311b2016-01-08 09:53:11 +0100131{
132 uint16_t i;
133 struct pollfd *pfd;
134 struct sockaddr_storage saddr;
135 socklen_t saddr_len = sizeof(saddr);
136 int ret, sock = -1;
137
138 pfd = malloc(bind_count * sizeof *pfd);
139 for (i = 0; i < bind_count; ++i) {
140 pfd[i].fd = binds[i].sock;
141 pfd[i].events = POLLIN;
142 pfd[i].revents = 0;
143 }
144
145 /* poll for a new connection */
146 errno = 0;
147 ret = poll(pfd, bind_count, timeout);
148 if (!ret) {
149 /* we timeouted */
150 free(pfd);
151 return 0;
152 } else if (ret == -1) {
153 ERR("%s: poll failed (%s)", __func__, strerror(errno));
154 free(pfd);
155 return -1;
156 }
157
158 for (i = 0; i < bind_count; ++i) {
159 if (pfd[i].revents & POLLIN) {
160 sock = pfd[i].fd;
161 break;
162 }
163 }
164 free(pfd);
165
166 if (sock == -1) {
167 ERR("%s: fatal error (%s:%d)", __func__, __FILE__, __LINE__);
168 return -1;
169 }
170
171 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
172 if (ret == -1) {
173 ERR("%s: accept failed (%s)", __func__, strerror(errno));
174 return -1;
175 }
176
Michal Vasko9e036d52016-01-08 10:49:26 +0100177 if (ti) {
178 *ti = binds[i].ti;
179 }
180
Michal Vasko086311b2016-01-08 09:53:11 +0100181 /* host was requested */
182 if (host) {
183 if (saddr.ss_family == AF_INET) {
184 *host = malloc(15);
185 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, 15)) {
186 ERR("%s: inet_ntop failed (%s)", __func__, strerror(errno));
187 free(*host);
188 *host = NULL;
189 }
190
191 if (port) {
192 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
193 }
194 } else if (saddr.ss_family == AF_INET6) {
195 *host = malloc(40);
196 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, 40)) {
197 ERR("%s: inet_ntop failed (%s)", __func__, strerror(errno));
198 free(*host);
199 *host = NULL;
200 }
201
202 if (port) {
203 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
204 }
205 } else {
206 ERR("%s: source host of an unknown protocol family", __func__);
207 }
208 }
209
210 return ret;
211}
212
Michal Vasko05ba9df2016-01-13 14:40:27 +0100213static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100214nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100215{
216 const char *identifier = NULL, *version = NULL, *format = NULL;
217 char *model_data = NULL;
218 const struct lys_module *module;
219 struct nc_server_error *err;
220 struct lyd_node *child, *data = NULL;
221 const struct lys_node *sdata;
222
223 LY_TREE_FOR(rpc->child, child) {
224 if (!strcmp(child->schema->name, "identifier")) {
225 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
226 } else if (!strcmp(child->schema->name, "version")) {
227 version = ((struct lyd_node_leaf_list *)child)->value_str;
228 } else if (!strcmp(child->schema->name, "format")) {
229 format = ((struct lyd_node_leaf_list *)child)->value_str;
230 }
231 }
232
233 /* check version */
234 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100235 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
236 nc_err_set_msg(err, "The requested version is not supported.", "en");
237 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100238 }
239
240 /* check and get module with the name identifier */
241 module = ly_ctx_get_module(server_opts.ctx, identifier, version);
242 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100243 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
244 nc_err_set_msg(err, "The requested schema was not found.", "en");
245 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100246 }
247
248 /* check format */
249 if (!format || !strcmp(format, "yang")) {
250 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL);
251 } else if (!strcmp(format, "yin")) {
252 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL);
253 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100254 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
255 nc_err_set_msg(err, "The requested format is not supported.", "en");
256 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100257 }
258
259 sdata = ly_ctx_get_node(server_opts.ctx, "/ietf-netconf-monitoring:get-schema/output/data");
260 if (model_data) {
261 data = lyd_output_new_anyxml(sdata, model_data);
262 }
263 free(model_data);
264 if (!data) {
265 ERRINT;
266 return NULL;
267 }
268
269 return nc_server_reply_data(data, NC_PARAMTYPE_FREE);
270}
271
272static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100273nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100274{
Michal Vasko428087d2016-01-14 16:04:28 +0100275 session->term_reason = NC_SESSION_TERM_CLOSED;
276 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100277}
278
Michal Vasko086311b2016-01-08 09:53:11 +0100279API int
280nc_server_init(struct ly_ctx *ctx)
281{
Michal Vasko05ba9df2016-01-13 14:40:27 +0100282 const struct lys_node *rpc;
283
Michal Vasko086311b2016-01-08 09:53:11 +0100284 if (!ctx) {
285 ERRARG;
286 return -1;
287 }
288
Michal Vasko05ba9df2016-01-13 14:40:27 +0100289 /* set default <get-schema> callback if not specified */
290 rpc = ly_ctx_get_node(ctx, "/ietf-netconf-monitoring:get-schema");
291 if (rpc && !rpc->private) {
292 lys_set_private(rpc, nc_clb_default_get_schema);
293 }
294
295 /* set default <close-session> callback if not specififed */
296 rpc = ly_ctx_get_node(ctx, "/ietf-netconf:close-session");
297 if (rpc && !rpc->private) {
298 lys_set_private(rpc, nc_clb_default_close_session);
299 }
300
Michal Vasko086311b2016-01-08 09:53:11 +0100301 server_opts.ctx = ctx;
302 return 0;
303}
304
305API int
306nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
307{
308 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)
309 || (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT)))) {
310 ERRARG;
311 return -1;
312 }
313
314 server_opts.wd_basic_mode = basic_mode;
315 server_opts.wd_also_supported = also_supported;
316 return 0;
317}
318
Michal Vasko1a38c862016-01-15 15:50:07 +0100319API void
Michal Vasko086311b2016-01-08 09:53:11 +0100320nc_server_set_capab_interleave(int interleave_support)
321{
322 if (interleave_support) {
323 server_opts.interleave_capab = 1;
324 } else {
325 server_opts.interleave_capab = 0;
326 }
Michal Vasko086311b2016-01-08 09:53:11 +0100327}
328
Michal Vasko1a38c862016-01-15 15:50:07 +0100329API void
Michal Vasko086311b2016-01-08 09:53:11 +0100330nc_server_set_hello_timeout(uint16_t hello_timeout)
331{
Michal Vasko086311b2016-01-08 09:53:11 +0100332 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100333}
334
Michal Vasko1a38c862016-01-15 15:50:07 +0100335API void
Michal Vasko086311b2016-01-08 09:53:11 +0100336nc_server_set_idle_timeout(uint16_t idle_timeout)
337{
Michal Vasko086311b2016-01-08 09:53:11 +0100338 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100339}
340
341API int
Michal Vasko1a38c862016-01-15 15:50:07 +0100342nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100343{
Michal Vasko1a38c862016-01-15 15:50:07 +0100344 if (fdin < 0 || fdout < 0 || !username || !session) {
345 ERRARG;
346 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100347 }
348
349 /* prepare session structure */
Michal Vasko1a38c862016-01-15 15:50:07 +0100350 *session = calloc(1, sizeof **session);
351 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100352 ERRMEM;
Michal Vasko1a38c862016-01-15 15:50:07 +0100353 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100354 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100355 (*session)->status = NC_STATUS_STARTING;
356 (*session)->side = NC_SERVER;
Michal Vasko086311b2016-01-08 09:53:11 +0100357
358 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100359 (*session)->ti_type = NC_TI_FD;
360 (*session)->ti.fd.in = fdin;
361 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100362
363 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100364 (*session)->flags = NC_SESSION_SHAREDCTX;
365 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100366
367 /* NETCONF handshake */
Michal Vasko1a38c862016-01-15 15:50:07 +0100368 (*session)->id = session_id++;
369 if (nc_handshake(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100370 goto fail;
371 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100372 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100373
Michal Vasko1a38c862016-01-15 15:50:07 +0100374 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100375
376fail:
Michal Vasko1a38c862016-01-15 15:50:07 +0100377 nc_session_free(*session);
378 *session = NULL;
379 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100380}
Michal Vasko9e036d52016-01-08 10:49:26 +0100381
Michal Vasko428087d2016-01-14 16:04:28 +0100382API struct nc_pollsession *
383nc_ps_new(void)
384{
385 return calloc(1, sizeof(struct nc_pollsession));
386}
387
388API void
389nc_ps_free(struct nc_pollsession *ps)
390{
391 free(ps->sessions);
392 free(ps);
393}
394
395API int
396nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
397{
398 if (!ps || !session) {
399 ERRARG;
400 return -1;
401 }
402
403 ++ps->session_count;
404 ps->sessions = realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
405
406 switch (session->ti_type) {
407 case NC_TI_FD:
408 ps->sessions[ps->session_count - 1].fd = session->ti.fd.in;
409 break;
410
411#ifdef ENABLE_SSH
412 case NC_TI_LIBSSH:
413 ps->sessions[ps->session_count - 1].fd = ssh_get_fd(session->ti.libssh.session);
414 break;
415#endif
416
417#ifdef ENABLE_TLS
418 case NC_TI_OPENSSL:
419 ps->sessions[ps->session_count - 1].fd = SSL_get_rfd(session->ti.tls);
420 break;
421#endif
422
423 default:
424 ERRINT;
425 return -1;
426 }
427 ps->sessions[ps->session_count - 1].events = POLLIN;
428 ps->sessions[ps->session_count - 1].revents = 0;
429 ps->sessions[ps->session_count - 1].session = session;
430
431 return 0;
432}
433
434API int
435nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
436{
437 uint16_t i;
438
439 if (!ps || !session) {
440 ERRARG;
441 return -1;
442 }
443
444 for (i = 0; i < ps->session_count; ++i) {
445 if (ps->sessions[i].session == session) {
446 --ps->session_count;
447 memmove(&ps->sessions[i], &ps->sessions[i + 1], ps->session_count - i);
448 return 0;
449 }
450 }
451
452 return 1;
453}
454
455/* must be called holding the session lock! */
456static NC_MSG_TYPE
457nc_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
458{
459 struct lyxml_elem *xml = NULL;
460 NC_MSG_TYPE msgtype;
461
462 if (!session || !rpc) {
463 ERRARG;
464 return NC_MSG_ERROR;
465 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
466 ERR("%s: invalid session to receive RPCs.", __func__);
467 return NC_MSG_ERROR;
468 }
469
470 msgtype = nc_read_msg(session, &xml);
471
472 switch (msgtype) {
473 case NC_MSG_RPC:
474 *rpc = malloc(sizeof **rpc);
475 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child, LYD_OPT_DESTRUCT | LYD_OPT_RPC);
476 (*rpc)->root = xml;
477 break;
478 case NC_MSG_HELLO:
479 ERR("%s: session %u: received another <hello> message.", __func__, session->id);
480 goto error;
481 case NC_MSG_REPLY:
482 ERR("%s: session %u: received <rpc-reply> from NETCONF client.", __func__, session->id);
483 goto error;
484 case NC_MSG_NOTIF:
485 ERR("%s: session %u: received <notification> from NETCONF client.", __func__, session->id);
486 goto error;
487 default:
488 /* NC_MSG_ERROR - pass it out;
489 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
490 */
491 break;
492 }
493
494 return msgtype;
495
496error:
497 /* cleanup */
498 lyxml_free(server_opts.ctx, xml);
499
500 return NC_MSG_ERROR;
501}
502
503/* must be called holding the session lock! */
504static NC_MSG_TYPE
505nc_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
506{
507 nc_rpc_clb clb;
508 struct nc_server_reply *reply;
509 int ret;
510
511 /* no callback, reply with a not-implemented error */
512 if (!rpc->tree->schema->private) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100513 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +0100514 } else {
515 clb = (nc_rpc_clb)rpc->tree->schema->private;
516 reply = clb(rpc->tree, session);
517 }
518
519 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100520 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +0100521 }
522
523 ret = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
524
525 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
526 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
527 session->status = NC_STATUS_INVALID;
528 }
529
530 if (ret == -1) {
531 ERR("%s: failed to write reply.", __func__);
532 nc_server_reply_free(reply);
533 return NC_MSG_ERROR;
534 }
535 nc_server_reply_free(reply);
536
537 return NC_MSG_REPLY;
538}
539
540API int
541nc_ps_poll(struct nc_pollsession *ps, int timeout)
542{
543 int ret;
544 uint16_t i;
545 NC_MSG_TYPE msgtype;
546 struct nc_session *session;
547 struct nc_server_rpc *rpc;
548 struct timespec old_ts, new_ts;
549
550 if (!ps || !ps->session_count) {
551 ERRARG;
552 return -1;
553 }
554
555 for (i = 0; i < ps->session_count; ++i) {
556 if (ps->sessions[i].session->status != NC_STATUS_RUNNING) {
557 ERR("%s: session %u: session not running.", __func__, ps->sessions[i].session->id);
558 return -1;
559 }
560 }
561
562 if (timeout > 0) {
563 clock_gettime(CLOCK_MONOTONIC_RAW, &old_ts);
564 }
565
566retry_poll:
567 ret = poll((struct pollfd *)ps->sessions, ps->session_count, timeout);
568 if (ret < 1) {
569 return ret;
570 }
571
572 /* find the first fd with POLLIN, we don't care if there are more */
573 for (i = 0; i < ps->session_count; ++i) {
574 if (ps->sessions[i].revents & POLLIN) {
575#ifdef ENABLE_SSH
576 if (ps->sessions[i].session->ti_type == NC_TI_LIBSSH) {
577 /* things are not that simple with SSH, we need to check the channel */
578 ret = ssh_channel_poll_timeout(ps->sessions[i].session->ti.libssh.channel, 0, 0);
579 /* not this one */
580 if (!ret) {
581 if (i == ps->session_count - 1) {
582 /* last session and it is not the right channel, ... */
583 if (timeout > 0) {
584 /* ... decrease timeout, wait it all out and try again, last time */
585 clock_gettime(CLOCK_MONOTONIC_RAW, &new_ts);
586
587 timeout -= (new_ts.tv_sec - old_ts.tv_sec) * 1000;
588 timeout -= (new_ts.tv_nsec - old_ts.tv_nsec) / 1000000;
589 if (timeout < 0) {
590 ERRINT;
591 return -1;
592 }
593
594 old_ts = new_ts;
595 } else if (!timeout) {
596 /* ... timeout is 0, so that is it */
597 return 0;
598 } else {
599 /* ... retry polling reasonable time apart */
600 usleep(NC_TIMEOUT_STEP);
601 goto retry_poll;
602 }
603 }
604 /* check other sessions */
605 continue;
606 } else if (ret == SSH_ERROR) {
607 ERR("%s: session %u: SSH channel error (%s).", __func__, ps->sessions[i].session->id,
608 ssh_get_error(ps->sessions[i].session->ti.libssh.session));
609 ps->sessions[i].session->status = NC_STATUS_INVALID;
610 ps->sessions[i].session->term_reason = NC_SESSION_TERM_OTHER;
611 return 2;
612 } else if (ret == SSH_EOF) {
613 ERR("%s: session %u: communication channel unexpectedly closed (libssh).",
614 __func__, ps->sessions[i].session->id);
615 ps->sessions[i].session->status = NC_STATUS_INVALID;
616 ps->sessions[i].session->term_reason = NC_SESSION_TERM_DROPPED;
617 return 2;
618 }
619 }
620#endif /* ENABLE_SSH */
621
622 break;
623 }
624 }
625
626 if (i == ps->session_count) {
627 ERRINT;
628 return -1;
629 }
630
631 /* this is the session with some data available for reading */
632 session = ps->sessions[i].session;
633
634 if (timeout > 0) {
635 clock_gettime(CLOCK_MONOTONIC_RAW, &new_ts);
636
637 /* subtract elapsed time */
638 timeout -= (new_ts.tv_sec - old_ts.tv_sec) * 1000;
639 timeout -= (new_ts.tv_nsec - old_ts.tv_nsec) / 1000000;
640 if (timeout < 0) {
641 ERRINT;
642 return -1;
643 }
644 }
645
646 /* reading an RPC and sending a reply must be atomic */
647 ret = session_ti_lock(session, timeout);
648 if (ret > 0) {
649 /* error */
650 return -1;
651 } else if (ret < 0) {
652 /* timeout */
653 return 0;
654 }
655
656 msgtype = nc_recv_rpc(session, &rpc);
657 if (msgtype == NC_MSG_ERROR) {
658 session_ti_unlock(session);
659 if (session->status != NC_STATUS_RUNNING) {
660 return 2;
661 }
662 return -1;
663 }
664
665 /* process RPC */
666 msgtype = nc_send_reply(session, rpc);
667
668 session_ti_unlock(session);
669
670 if (msgtype == NC_MSG_ERROR) {
671 nc_server_rpc_free(rpc);
672 if (session->status != NC_STATUS_RUNNING) {
673 return 2;
674 }
675 return -1;
676 }
677
678 nc_server_rpc_free(rpc);
679 return 1;
680}
681
Michal Vasko9e036d52016-01-08 10:49:26 +0100682#if defined(ENABLE_SSH) || defined(ENABLE_TLS)
683
684API int
685nc_server_add_bind_listen(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
686{
687 int sock;
688
689 if (!address || !port || ((ti != NC_TI_LIBSSH) && (ti != NC_TI_OPENSSL))) {
690 ERRARG;
691 return -1;
692 }
693
694 sock = nc_sock_listen(address, port);
695 if (sock == -1) {
696 return -1;
697 }
698
699 ++server_opts.bind_count;
700 server_opts.binds = realloc(server_opts.binds, server_opts.bind_count * sizeof *server_opts.binds);
701
702 server_opts.binds[server_opts.bind_count - 1].address = strdup(address);
703 server_opts.binds[server_opts.bind_count - 1].port = port;
704 server_opts.binds[server_opts.bind_count - 1].sock = sock;
705 server_opts.binds[server_opts.bind_count - 1].ti = ti;
706
707 return 0;
708}
709
710API int
711nc_server_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
712{
713 uint32_t i;
714 int ret = -1;
715
Michal Vasko1a38c862016-01-15 15:50:07 +0100716 if (!address && !port && !ti) {
717 for (i = 0; i < server_opts.bind_count; ++i) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100718 close(server_opts.binds[i].sock);
719 free(server_opts.binds[i].address);
720
Michal Vasko9e036d52016-01-08 10:49:26 +0100721 ret = 0;
722 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100723 free(server_opts.binds);
724 server_opts.binds = NULL;
725 server_opts.bind_count = 0;
726 } else {
727 for (i = 0; i < server_opts.bind_count; ++i) {
728 if ((!address || !strcmp(server_opts.binds[i].address, address))
729 && (!port || (server_opts.binds[i].port == port))
730 && (!ti || (server_opts.binds[i].ti == ti))) {
731 close(server_opts.binds[i].sock);
732 free(server_opts.binds[i].address);
733
734 --server_opts.bind_count;
735 memmove(&server_opts.binds[i], &server_opts.binds[i + 1], (server_opts.bind_count - i) * sizeof *server_opts.binds);
736
737 ret = 0;
738 }
739 }
Michal Vasko9e036d52016-01-08 10:49:26 +0100740 }
741
742 return ret;
743}
744
Michal Vasko1a38c862016-01-15 15:50:07 +0100745API int
746nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +0100747{
748 NC_TRANSPORT_IMPL ti;
Michal Vasko1a38c862016-01-15 15:50:07 +0100749 int sock, ret;
Michal Vasko9e036d52016-01-08 10:49:26 +0100750 char *host;
751 uint16_t port;
Michal Vasko9e036d52016-01-08 10:49:26 +0100752
Michal Vasko1a38c862016-01-15 15:50:07 +0100753 if (!server_opts.ctx || !server_opts.binds || !session) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100754 ERRARG;
Michal Vasko1a38c862016-01-15 15:50:07 +0100755 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +0100756 }
757
758 sock = nc_sock_accept(server_opts.binds, server_opts.bind_count, timeout, &ti, &host, &port);
Michal Vasko1a38c862016-01-15 15:50:07 +0100759 if (sock < 1) {
760 return sock;
Michal Vasko9e036d52016-01-08 10:49:26 +0100761 }
762
Michal Vasko1a38c862016-01-15 15:50:07 +0100763 *session = calloc(1, sizeof **session);
Michal Vasko9e036d52016-01-08 10:49:26 +0100764 if (!session) {
765 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100766 close(sock);
Michal Vasko1a38c862016-01-15 15:50:07 +0100767 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +0100768 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100769 (*session)->status = NC_STATUS_STARTING;
770 (*session)->side = NC_SERVER;
771 (*session)->ctx = server_opts.ctx;
772 (*session)->flags = NC_SESSION_SHAREDCTX;
773 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
774 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +0100775
776 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +0100777 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
778 if (!(*session)->ti_lock) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100779 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100780 close(sock);
Michal Vasko1a38c862016-01-15 15:50:07 +0100781 ret = -1;
Michal Vasko9e036d52016-01-08 10:49:26 +0100782 goto fail;
783 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100784 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +0100785
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100786 /* sock gets assigned to session or closed */
Michal Vasko9e036d52016-01-08 10:49:26 +0100787 if (ti == NC_TI_LIBSSH) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100788 ret = nc_accept_ssh_session(*session, sock, timeout);
789 if (ret < 1) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100790 goto fail;
791 }
792 } else if (ti == NC_TI_OPENSSL) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100793 ret = nc_accept_tls_session(*session, sock, timeout);
794 if (ret < 1) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100795 goto fail;
796 }
797 } else {
798 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100799 close(sock);
Michal Vasko1a38c862016-01-15 15:50:07 +0100800 ret = -1;
Michal Vasko9e036d52016-01-08 10:49:26 +0100801 goto fail;
802 }
803
804 /* NETCONF handshake */
Michal Vasko1a38c862016-01-15 15:50:07 +0100805 (*session)->id = session_id++;
806 if (nc_handshake(*session)) {
807 ret = -1;
Michal Vasko9e036d52016-01-08 10:49:26 +0100808 goto fail;
809 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100810 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +0100811
Michal Vasko1a38c862016-01-15 15:50:07 +0100812 return 1;
Michal Vasko9e036d52016-01-08 10:49:26 +0100813
814fail:
Michal Vasko1a38c862016-01-15 15:50:07 +0100815 nc_session_free(*session);
816 *session = NULL;
817 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +0100818}
819
820#endif /* ENABLE_SSH || ENABLE_TLS */