blob: d1d643f325d5090641758092e6038feb41d6b331 [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>
Michal Vaskob48aa812016-01-18 14:13:09 +010033#include <pthread.h>
Michal Vasko11d142a2016-01-19 15:58:24 +010034#include <time.h>
Michal Vasko086311b2016-01-08 09:53:11 +010035
Michal Vasko1a38c862016-01-15 15:50:07 +010036#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010037#include "session_server.h"
38
Michal Vaskob48aa812016-01-18 14:13:09 +010039struct nc_server_opts server_opts = {
Michal Vasko3031aae2016-01-27 16:07:18 +010040 .endpt_array_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010041};
Michal Vasko3031aae2016-01-27 16:07:18 +010042extern struct nc_server_ssh_opts ssh_ch_opts;
43extern struct nc_server_tls_opts tls_ch_opts;
44
45struct nc_endpt *
46nc_server_get_endpt(const char *name, NC_TRANSPORT_IMPL ti)
47{
48 uint16_t i;
49
50 for (i = 0; i < server_opts.endpt_count; ++i) {
51 if ((server_opts.binds[i].ti == ti) && !strcmp(server_opts.endpts[i].name, name)) {
52 return &server_opts.endpts[i];
53 }
54 }
55
56 return NULL;
57}
Michal Vasko086311b2016-01-08 09:53:11 +010058
Michal Vasko1a38c862016-01-15 15:50:07 +010059API void
60nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
61{
62 if (!session || !reason) {
63 ERRARG;
64 return;
65 }
66
67 session->term_reason = reason;
68}
69
Michal Vasko086311b2016-01-08 09:53:11 +010070int
Michal Vaskof05562c2016-01-20 12:06:43 +010071nc_sock_listen(const char *address, uint16_t port)
Michal Vasko086311b2016-01-08 09:53:11 +010072{
73 const int optVal = 1;
74 const socklen_t optLen = sizeof(optVal);
75 int is_ipv4, sock;
76 struct sockaddr_storage saddr;
77
78 struct sockaddr_in *saddr4;
79 struct sockaddr_in6 *saddr6;
80
81
82 if (!strchr(address, ':')) {
83 is_ipv4 = 1;
84 } else {
85 is_ipv4 = 0;
86 }
87
88 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
89 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +010090 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +010091 goto fail;
92 }
93
94 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&optVal, optLen)) {
Michal Vaskod083db62016-01-19 10:31:29 +010095 ERR("Could not set socket SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +010096 goto fail;
97 }
98
99 bzero(&saddr, sizeof(struct sockaddr_storage));
100 if (is_ipv4) {
101 saddr4 = (struct sockaddr_in *)&saddr;
102
103 saddr4->sin_family = AF_INET;
104 saddr4->sin_port = htons(port);
105
106 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100107 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100108 goto fail;
109 }
110
111 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100112 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100113 goto fail;
114 }
115
116 } else {
117 saddr6 = (struct sockaddr_in6 *)&saddr;
118
119 saddr6->sin6_family = AF_INET6;
120 saddr6->sin6_port = htons(port);
121
122 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100123 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100124 goto fail;
125 }
126
127 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100128 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100129 goto fail;
130 }
131 }
132
Michal Vaskofb89d772016-01-08 12:25:35 +0100133 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100134 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100135 goto fail;
136 }
137
138 return sock;
139
140fail:
141 if (sock > -1) {
142 close(sock);
143 }
144
145 return -1;
146}
147
148int
Michal Vasko3031aae2016-01-27 16:07:18 +0100149nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, int timeout, char **host, uint16_t *port, uint16_t *idx)
Michal Vasko086311b2016-01-08 09:53:11 +0100150{
151 uint16_t i;
152 struct pollfd *pfd;
153 struct sockaddr_storage saddr;
154 socklen_t saddr_len = sizeof(saddr);
155 int ret, sock = -1;
156
157 pfd = malloc(bind_count * sizeof *pfd);
158 for (i = 0; i < bind_count; ++i) {
159 pfd[i].fd = binds[i].sock;
160 pfd[i].events = POLLIN;
161 pfd[i].revents = 0;
162 }
163
164 /* poll for a new connection */
165 errno = 0;
166 ret = poll(pfd, bind_count, timeout);
167 if (!ret) {
168 /* we timeouted */
169 free(pfd);
170 return 0;
171 } else if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100172 ERR("Poll failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100173 free(pfd);
174 return -1;
175 }
176
177 for (i = 0; i < bind_count; ++i) {
178 if (pfd[i].revents & POLLIN) {
179 sock = pfd[i].fd;
180 break;
181 }
182 }
183 free(pfd);
184
185 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100186 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100187 return -1;
188 }
189
190 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100191 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100192 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100193 return -1;
194 }
195
Michal Vasko3031aae2016-01-27 16:07:18 +0100196 if (idx) {
197 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100198 }
199
Michal Vasko086311b2016-01-08 09:53:11 +0100200 /* host was requested */
201 if (host) {
202 if (saddr.ss_family == AF_INET) {
203 *host = malloc(15);
204 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, 15)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100205 ERR("inet_ntop failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100206 free(*host);
207 *host = NULL;
208 }
209
210 if (port) {
211 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
212 }
213 } else if (saddr.ss_family == AF_INET6) {
214 *host = malloc(40);
215 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, 40)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100216 ERR("inet_ntop failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100217 free(*host);
218 *host = NULL;
219 }
220
221 if (port) {
222 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
223 }
224 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100225 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100226 }
227 }
228
229 return ret;
230}
231
Michal Vasko05ba9df2016-01-13 14:40:27 +0100232static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100233nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100234{
235 const char *identifier = NULL, *version = NULL, *format = NULL;
236 char *model_data = NULL;
237 const struct lys_module *module;
238 struct nc_server_error *err;
239 struct lyd_node *child, *data = NULL;
Michal Vasko11d142a2016-01-19 15:58:24 +0100240 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100241
242 LY_TREE_FOR(rpc->child, child) {
243 if (!strcmp(child->schema->name, "identifier")) {
244 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
245 } else if (!strcmp(child->schema->name, "version")) {
246 version = ((struct lyd_node_leaf_list *)child)->value_str;
247 } else if (!strcmp(child->schema->name, "format")) {
248 format = ((struct lyd_node_leaf_list *)child)->value_str;
249 }
250 }
251
252 /* check version */
253 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
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 version is not supported.", "en");
256 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100257 }
258
259 /* check and get module with the name identifier */
260 module = ly_ctx_get_module(server_opts.ctx, identifier, version);
261 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100262 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
263 nc_err_set_msg(err, "The requested schema was not found.", "en");
264 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100265 }
266
267 /* check format */
268 if (!format || !strcmp(format, "yang")) {
269 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL);
270 } else if (!strcmp(format, "yin")) {
271 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL);
272 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100273 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
274 nc_err_set_msg(err, "The requested format is not supported.", "en");
275 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100276 }
277
Michal Vasko0473c4c2016-01-19 10:40:06 +0100278 module = ly_ctx_get_module(server_opts.ctx, "ietf-netconf-monitoring", NULL);
279 if (module) {
280 sdata = lys_get_node(module, "/get-schema/output/data");
281 }
282 if (model_data && sdata) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100283 nc_ctx_lock(-1, NULL);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100284 data = lyd_output_new_anyxml(sdata, model_data);
Michal Vasko11d142a2016-01-19 15:58:24 +0100285 nc_ctx_unlock();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100286 }
287 free(model_data);
288 if (!data) {
289 ERRINT;
290 return NULL;
291 }
292
293 return nc_server_reply_data(data, NC_PARAMTYPE_FREE);
294}
295
296static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100297nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100298{
Michal Vasko428087d2016-01-14 16:04:28 +0100299 session->term_reason = NC_SESSION_TERM_CLOSED;
300 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100301}
302
Michal Vasko086311b2016-01-08 09:53:11 +0100303API int
304nc_server_init(struct ly_ctx *ctx)
305{
Michal Vasko05ba9df2016-01-13 14:40:27 +0100306 const struct lys_node *rpc;
Michal Vasko0473c4c2016-01-19 10:40:06 +0100307 const struct lys_module *mod;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100308
Michal Vasko086311b2016-01-08 09:53:11 +0100309 if (!ctx) {
310 ERRARG;
311 return -1;
312 }
313
Michal Vasko05ba9df2016-01-13 14:40:27 +0100314 /* set default <get-schema> callback if not specified */
Michal Vasko0473c4c2016-01-19 10:40:06 +0100315 rpc = NULL;
316 mod = ly_ctx_get_module(ctx, "ietf-netconf-monitoring", NULL);
317 if (mod) {
318 rpc = lys_get_node(mod, "/get-schema");
319 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100320 if (rpc && !rpc->private) {
321 lys_set_private(rpc, nc_clb_default_get_schema);
322 }
323
324 /* set default <close-session> callback if not specififed */
Michal Vasko0473c4c2016-01-19 10:40:06 +0100325 rpc = NULL;
326 mod = ly_ctx_get_module(ctx, "ietf-netconf", NULL);
327 if (mod) {
328 rpc = lys_get_node(mod, "/close-session");
329 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100330 if (rpc && !rpc->private) {
331 lys_set_private(rpc, nc_clb_default_close_session);
332 }
333
Michal Vasko086311b2016-01-08 09:53:11 +0100334 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100335
336 server_opts.new_session_id = 1;
337 pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
338
Michal Vasko086311b2016-01-08 09:53:11 +0100339 return 0;
340}
341
Michal Vaskob48aa812016-01-18 14:13:09 +0100342API void
343nc_server_destroy(void)
344{
345 pthread_spin_destroy(&server_opts.sid_lock);
346
347#if defined(ENABLE_SSH) || defined(ENABLE_TLS)
Michal Vasko3031aae2016-01-27 16:07:18 +0100348 nc_server_del_endpt(NULL, 0);
Michal Vaskob48aa812016-01-18 14:13:09 +0100349#endif
350}
351
Michal Vasko086311b2016-01-08 09:53:11 +0100352API int
353nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
354{
355 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)
356 || (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT)))) {
357 ERRARG;
358 return -1;
359 }
360
361 server_opts.wd_basic_mode = basic_mode;
362 server_opts.wd_also_supported = also_supported;
363 return 0;
364}
365
Michal Vasko1a38c862016-01-15 15:50:07 +0100366API void
Michal Vasko086311b2016-01-08 09:53:11 +0100367nc_server_set_capab_interleave(int interleave_support)
368{
369 if (interleave_support) {
370 server_opts.interleave_capab = 1;
371 } else {
372 server_opts.interleave_capab = 0;
373 }
Michal Vasko086311b2016-01-08 09:53:11 +0100374}
375
Michal Vasko1a38c862016-01-15 15:50:07 +0100376API void
Michal Vasko086311b2016-01-08 09:53:11 +0100377nc_server_set_hello_timeout(uint16_t hello_timeout)
378{
Michal Vasko086311b2016-01-08 09:53:11 +0100379 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100380}
381
Michal Vasko1a38c862016-01-15 15:50:07 +0100382API void
Michal Vasko086311b2016-01-08 09:53:11 +0100383nc_server_set_idle_timeout(uint16_t idle_timeout)
384{
Michal Vasko086311b2016-01-08 09:53:11 +0100385 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100386}
387
388API int
Michal Vasko1a38c862016-01-15 15:50:07 +0100389nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100390{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100391 if (!server_opts.ctx || (fdin < 0) || (fdout < 0) || !username || !session) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100392 ERRARG;
393 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100394 }
395
396 /* prepare session structure */
Michal Vasko1a38c862016-01-15 15:50:07 +0100397 *session = calloc(1, sizeof **session);
398 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100399 ERRMEM;
Michal Vasko1a38c862016-01-15 15:50:07 +0100400 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100401 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100402 (*session)->status = NC_STATUS_STARTING;
403 (*session)->side = NC_SERVER;
Michal Vasko086311b2016-01-08 09:53:11 +0100404
405 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100406 (*session)->ti_type = NC_TI_FD;
407 (*session)->ti.fd.in = fdin;
408 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100409
410 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100411 (*session)->flags = NC_SESSION_SHAREDCTX;
412 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100413
Michal Vaskob48aa812016-01-18 14:13:09 +0100414 /* assign new SID atomically */
415 pthread_spin_lock(&server_opts.sid_lock);
416 (*session)->id = server_opts.new_session_id++;
417 pthread_spin_unlock(&server_opts.sid_lock);
418
Michal Vasko086311b2016-01-08 09:53:11 +0100419 /* NETCONF handshake */
Michal Vasko1a38c862016-01-15 15:50:07 +0100420 if (nc_handshake(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100421 goto fail;
422 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100423 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100424 (*session)->last_rpc = time(NULL);
Michal Vasko086311b2016-01-08 09:53:11 +0100425
Michal Vasko1a38c862016-01-15 15:50:07 +0100426 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100427
428fail:
Michal Vasko1a38c862016-01-15 15:50:07 +0100429 nc_session_free(*session);
430 *session = NULL;
431 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100432}
Michal Vasko9e036d52016-01-08 10:49:26 +0100433
Michal Vasko428087d2016-01-14 16:04:28 +0100434API struct nc_pollsession *
435nc_ps_new(void)
436{
437 return calloc(1, sizeof(struct nc_pollsession));
438}
439
440API void
441nc_ps_free(struct nc_pollsession *ps)
442{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100443 if (!ps) {
444 return;
445 }
446
Michal Vasko3a715132016-01-21 15:40:31 +0100447 free(ps->pfds);
Michal Vasko428087d2016-01-14 16:04:28 +0100448 free(ps->sessions);
449 free(ps);
450}
451
452API int
453nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
454{
455 if (!ps || !session) {
456 ERRARG;
457 return -1;
458 }
459
460 ++ps->session_count;
Michal Vasko3a715132016-01-21 15:40:31 +0100461 ps->pfds = realloc(ps->pfds, ps->session_count * sizeof *ps->pfds);
Michal Vasko428087d2016-01-14 16:04:28 +0100462 ps->sessions = realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
463
464 switch (session->ti_type) {
465 case NC_TI_FD:
Michal Vasko3a715132016-01-21 15:40:31 +0100466 ps->pfds[ps->session_count - 1].fd = session->ti.fd.in;
Michal Vasko428087d2016-01-14 16:04:28 +0100467 break;
468
469#ifdef ENABLE_SSH
470 case NC_TI_LIBSSH:
Michal Vasko3a715132016-01-21 15:40:31 +0100471 ps->pfds[ps->session_count - 1].fd = ssh_get_fd(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100472 break;
473#endif
474
475#ifdef ENABLE_TLS
476 case NC_TI_OPENSSL:
Michal Vasko3a715132016-01-21 15:40:31 +0100477 ps->pfds[ps->session_count - 1].fd = SSL_get_rfd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100478 break;
479#endif
480
481 default:
482 ERRINT;
483 return -1;
484 }
Michal Vasko3a715132016-01-21 15:40:31 +0100485 ps->pfds[ps->session_count - 1].events = POLLIN;
486 ps->pfds[ps->session_count - 1].revents = 0;
487 ps->sessions[ps->session_count - 1] = session;
Michal Vasko428087d2016-01-14 16:04:28 +0100488
489 return 0;
490}
491
492API int
493nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
494{
495 uint16_t i;
496
497 if (!ps || !session) {
498 ERRARG;
499 return -1;
500 }
501
502 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100503 if (ps->sessions[i] == session) {
Michal Vasko428087d2016-01-14 16:04:28 +0100504 --ps->session_count;
Michal Vasko3a715132016-01-21 15:40:31 +0100505 ps->sessions[i] = ps->sessions[ps->session_count];
506 memcpy(&ps->pfds[i], &ps->pfds[ps->session_count], sizeof *ps->pfds);
Michal Vasko428087d2016-01-14 16:04:28 +0100507 return 0;
508 }
509 }
510
511 return 1;
512}
513
514/* must be called holding the session lock! */
515static NC_MSG_TYPE
516nc_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
517{
518 struct lyxml_elem *xml = NULL;
519 NC_MSG_TYPE msgtype;
520
521 if (!session || !rpc) {
522 ERRARG;
523 return NC_MSG_ERROR;
524 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100525 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100526 return NC_MSG_ERROR;
527 }
528
529 msgtype = nc_read_msg(session, &xml);
530
531 switch (msgtype) {
532 case NC_MSG_RPC:
533 *rpc = malloc(sizeof **rpc);
Michal Vasko11d142a2016-01-19 15:58:24 +0100534 nc_ctx_lock(-1, NULL);
Michal Vasko428087d2016-01-14 16:04:28 +0100535 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child, LYD_OPT_DESTRUCT | LYD_OPT_RPC);
Michal Vasko11d142a2016-01-19 15:58:24 +0100536 nc_ctx_unlock();
Michal Vasko428087d2016-01-14 16:04:28 +0100537 (*rpc)->root = xml;
538 break;
539 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +0100540 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100541 goto error;
542 case NC_MSG_REPLY:
Michal Vaskod083db62016-01-19 10:31:29 +0100543 ERR("Session %u: received <rpc-reply> from NETCONF client.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100544 goto error;
545 case NC_MSG_NOTIF:
Michal Vaskod083db62016-01-19 10:31:29 +0100546 ERR("Session %u: received <notification> from NETCONF client.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100547 goto error;
548 default:
549 /* NC_MSG_ERROR - pass it out;
550 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
551 */
552 break;
553 }
554
555 return msgtype;
556
557error:
558 /* cleanup */
559 lyxml_free(server_opts.ctx, xml);
560
561 return NC_MSG_ERROR;
562}
563
564/* must be called holding the session lock! */
565static NC_MSG_TYPE
566nc_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
567{
568 nc_rpc_clb clb;
569 struct nc_server_reply *reply;
570 int ret;
571
572 /* no callback, reply with a not-implemented error */
573 if (!rpc->tree->schema->private) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100574 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +0100575 } else {
576 clb = (nc_rpc_clb)rpc->tree->schema->private;
577 reply = clb(rpc->tree, session);
578 }
579
580 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100581 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +0100582 }
583
584 ret = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
585
586 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
587 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
588 session->status = NC_STATUS_INVALID;
589 }
590
591 if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100592 ERR("Session %u: failed to write reply.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100593 nc_server_reply_free(reply);
594 return NC_MSG_ERROR;
595 }
596 nc_server_reply_free(reply);
597
598 return NC_MSG_REPLY;
599}
600
601API int
602nc_ps_poll(struct nc_pollsession *ps, int timeout)
603{
604 int ret;
Michal Vasko96164bf2016-01-21 15:41:58 +0100605 uint16_t i, j;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100606 time_t cur_time;
Michal Vasko428087d2016-01-14 16:04:28 +0100607 NC_MSG_TYPE msgtype;
608 struct nc_session *session;
609 struct nc_server_rpc *rpc;
Michal Vasko96164bf2016-01-21 15:41:58 +0100610 struct timespec old_ts;
Michal Vasko428087d2016-01-14 16:04:28 +0100611
612 if (!ps || !ps->session_count) {
613 ERRARG;
614 return -1;
615 }
616
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100617 cur_time = time(NULL);
618
Michal Vasko428087d2016-01-14 16:04:28 +0100619 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100620 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
621 ERR("Session %u: session not running.", ps->sessions[i]->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100622 return -1;
623 }
Michal Vaskobd8ef262016-01-20 11:09:27 +0100624
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100625 /* TODO invalidate only sessions without subscription */
Michal Vasko3a715132016-01-21 15:40:31 +0100626 if (server_opts.idle_timeout && (ps->sessions[i]->last_rpc + server_opts.idle_timeout >= cur_time)) {
627 ERR("Session %u: session idle timeout elapsed.", ps->sessions[i]->id);
628 ps->sessions[i]->status = NC_STATUS_INVALID;
629 ps->sessions[i]->term_reason = NC_SESSION_TERM_TIMEOUT;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100630 return 3;
631 }
632
Michal Vasko3a715132016-01-21 15:40:31 +0100633 if (ps->pfds[i].revents) {
Michal Vaskobd8ef262016-01-20 11:09:27 +0100634 break;
635 }
Michal Vasko428087d2016-01-14 16:04:28 +0100636 }
637
638 if (timeout > 0) {
639 clock_gettime(CLOCK_MONOTONIC_RAW, &old_ts);
640 }
641
Michal Vaskobd8ef262016-01-20 11:09:27 +0100642 if (i == ps->session_count) {
Michal Vasko3a715132016-01-21 15:40:31 +0100643retry_poll:
Michal Vaskobd8ef262016-01-20 11:09:27 +0100644 /* no leftover event */
645 i = 0;
Michal Vasko3a715132016-01-21 15:40:31 +0100646 ret = poll(ps->pfds, ps->session_count, timeout);
Michal Vaskobd8ef262016-01-20 11:09:27 +0100647 if (ret < 1) {
648 return ret;
649 }
Michal Vasko428087d2016-01-14 16:04:28 +0100650 }
651
Michal Vaskobd8ef262016-01-20 11:09:27 +0100652 /* find the first fd with POLLIN, we don't care if there are more now */
653 for (; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100654 if (ps->pfds[i].revents & POLLHUP) {
655 ERR("Session %u: communication socket unexpectedly closed.", ps->sessions[i]->id);
656 ps->sessions[i]->status = NC_STATUS_INVALID;
657 ps->sessions[i]->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vaskobd8ef262016-01-20 11:09:27 +0100658 return 3;
Michal Vasko3a715132016-01-21 15:40:31 +0100659 } else if (ps->pfds[i].revents & POLLERR) {
660 ERR("Session %u: communication socket error.", ps->sessions[i]->id);
661 ps->sessions[i]->status = NC_STATUS_INVALID;
662 ps->sessions[i]->term_reason = NC_SESSION_TERM_OTHER;
Michal Vaskobd8ef262016-01-20 11:09:27 +0100663 return 3;
Michal Vasko3a715132016-01-21 15:40:31 +0100664 } else if (ps->pfds[i].revents & POLLIN) {
Michal Vasko428087d2016-01-14 16:04:28 +0100665#ifdef ENABLE_SSH
Michal Vasko96164bf2016-01-21 15:41:58 +0100666 if (ps->sessions[i]->ti_type == NC_TI_LIBSSH) {
667 /* things are not that simple with SSH... */
668 ret = nc_ssh_pollin(ps->sessions[i], &timeout);
669
670 /* clear POLLIN on sessions sharing this session's SSH session */
671 if ((ret == 1) || (ret >= 4)) {
672 for (j = i + 1; j < ps->session_count; ++j) {
673 if (ps->pfds[j].fd == ps->pfds[i].fd) {
674 ps->pfds[j].revents = 0;
675 }
676 }
677 }
678
679 /* actual event happened */
680 if ((ret <= 0) || (ret >= 3)) {
681 ps->pfds[i].revents = 0;
682 return ret;
683
684 /* event occurred on some other channel */
685 } else if (ret == 2) {
686 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100687 if (i == ps->session_count - 1) {
688 /* last session and it is not the right channel, ... */
689 if (timeout > 0) {
690 /* ... decrease timeout, wait it all out and try again, last time */
Michal Vasko96164bf2016-01-21 15:41:58 +0100691 nc_subtract_elapsed(&timeout, &old_ts);
692 usleep(timeout * 1000);
693 timeout = 0;
694 goto retry_poll;
Michal Vasko428087d2016-01-14 16:04:28 +0100695 } else if (!timeout) {
696 /* ... timeout is 0, so that is it */
697 return 0;
698 } else {
699 /* ... retry polling reasonable time apart */
700 usleep(NC_TIMEOUT_STEP);
701 goto retry_poll;
702 }
703 }
704 /* check other sessions */
705 continue;
Michal Vasko428087d2016-01-14 16:04:28 +0100706 }
707 }
708#endif /* ENABLE_SSH */
709
Michal Vaskobd8ef262016-01-20 11:09:27 +0100710 /* we are going to process it now */
Michal Vasko3a715132016-01-21 15:40:31 +0100711 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100712 break;
713 }
714 }
715
716 if (i == ps->session_count) {
717 ERRINT;
718 return -1;
719 }
720
721 /* this is the session with some data available for reading */
Michal Vasko3a715132016-01-21 15:40:31 +0100722 session = ps->sessions[i];
Michal Vasko428087d2016-01-14 16:04:28 +0100723
724 if (timeout > 0) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100725 nc_subtract_elapsed(&timeout, &old_ts);
Michal Vasko428087d2016-01-14 16:04:28 +0100726 }
727
Michal Vaskobd8ef262016-01-20 11:09:27 +0100728 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100729 ret = nc_timedlock(session->ti_lock, timeout, NULL);
730 if (ret != 1) {
731 /* error or timeout */
732 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100733 }
734
735 msgtype = nc_recv_rpc(session, &rpc);
736 if (msgtype == NC_MSG_ERROR) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100737 pthread_mutex_unlock(session->ti_lock);
Michal Vasko428087d2016-01-14 16:04:28 +0100738 if (session->status != NC_STATUS_RUNNING) {
Michal Vaskobd8ef262016-01-20 11:09:27 +0100739 return 3;
Michal Vasko428087d2016-01-14 16:04:28 +0100740 }
741 return -1;
742 }
743
744 /* process RPC */
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100745 session->last_rpc = time(NULL);
Michal Vasko428087d2016-01-14 16:04:28 +0100746 msgtype = nc_send_reply(session, rpc);
747
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100748 pthread_mutex_unlock(session->ti_lock);
Michal Vasko428087d2016-01-14 16:04:28 +0100749
750 if (msgtype == NC_MSG_ERROR) {
751 nc_server_rpc_free(rpc);
Michal Vasko428087d2016-01-14 16:04:28 +0100752 return -1;
753 }
Michal Vasko428087d2016-01-14 16:04:28 +0100754 nc_server_rpc_free(rpc);
Michal Vaskobd8ef262016-01-20 11:09:27 +0100755
Michal Vaskobd8b4e12016-01-22 16:11:20 +0100756 /* status change takes precedence over leftover events (return 2) */
757 if (session->status != NC_STATUS_RUNNING) {
758 return 3;
759 }
760
Michal Vaskobd8ef262016-01-20 11:09:27 +0100761 /* is there some other socket waiting? */
762 for (++i; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100763 if (ps->pfds[i].revents) {
Michal Vaskobd8ef262016-01-20 11:09:27 +0100764 return 2;
765 }
766 }
767
Michal Vasko428087d2016-01-14 16:04:28 +0100768 return 1;
769}
770
Michal Vasko11d142a2016-01-19 15:58:24 +0100771API int
772nc_ctx_lock(int timeout, int *elapsed)
773{
774 return nc_timedlock(&server_opts.ctx_lock, timeout, elapsed);
775}
776
777API int
778nc_ctx_unlock(void)
779{
780 int ret;
781
782 ret = pthread_mutex_unlock(&server_opts.ctx_lock);
783
784 if (ret) {
785 ERR("Mutex unlock failed (%s).", strerror(ret));
786 return -1;
787 }
788
789 return 0;
790}
791
Michal Vasko9e036d52016-01-08 10:49:26 +0100792#if defined(ENABLE_SSH) || defined(ENABLE_TLS)
793
Michal Vasko3031aae2016-01-27 16:07:18 +0100794int
795nc_server_add_endpt_listen(const char *name, const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +0100796{
797 int sock;
Michal Vasko3031aae2016-01-27 16:07:18 +0100798 uint16_t i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100799
Michal Vasko3031aae2016-01-27 16:07:18 +0100800 if (!name || !address || !port) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100801 ERRARG;
802 return -1;
803 }
804
Michal Vasko3031aae2016-01-27 16:07:18 +0100805 /* READ LOCK */
806 pthread_rwlock_rdlock(&server_opts.endpt_array_lock);
807
808 /* check name uniqueness */
809 for (i = 0; i < server_opts.endpt_count; ++i) {
810 if (!strcmp(server_opts.endpts[i].name, name)) {
811 ERR("Endpoint \"%s\" already exists.", name);
812 return -1;
813 }
814 }
815
816 /* READ UNLOCK */
817 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
818
Michal Vasko9e036d52016-01-08 10:49:26 +0100819 sock = nc_sock_listen(address, port);
820 if (sock == -1) {
821 return -1;
822 }
823
Michal Vasko3031aae2016-01-27 16:07:18 +0100824 /* WRITE LOCK */
825 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +0100826
Michal Vasko3031aae2016-01-27 16:07:18 +0100827 ++server_opts.endpt_count;
828 server_opts.binds = realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
829 server_opts.endpts = realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vasko9e036d52016-01-08 10:49:26 +0100830
Michal Vasko11d142a2016-01-19 15:58:24 +0100831 nc_ctx_lock(-1, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100832 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
833 server_opts.binds[server_opts.endpt_count - 1].address = lydict_insert(server_opts.ctx, address, 0);
Michal Vasko11d142a2016-01-19 15:58:24 +0100834 nc_ctx_unlock();
Michal Vasko3031aae2016-01-27 16:07:18 +0100835 server_opts.binds[server_opts.endpt_count - 1].port = port;
836 server_opts.binds[server_opts.endpt_count - 1].sock = sock;
837 server_opts.binds[server_opts.endpt_count - 1].ti = ti;
838 switch (ti) {
839#ifdef ENABLE_SSH
840 case NC_TI_LIBSSH:
841 server_opts.endpts[server_opts.endpt_count - 1].ti_opts = calloc(1, sizeof(struct nc_server_ssh_opts));
842 break;
843#endif
844#ifdef ENABLE_TLS
845 case NC_TI_OPENSSL:
846 server_opts.endpts[server_opts.endpt_count - 1].ti_opts = calloc(1, sizeof(struct nc_server_tls_opts));
847 break;
848#endif
849 default:
850 ERRINT;
851 server_opts.endpts[server_opts.endpt_count - 1].ti_opts = NULL;
852 break;
853 }
854 pthread_mutex_init(&server_opts.endpts[server_opts.endpt_count - 1].endpt_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +0100855
Michal Vasko3031aae2016-01-27 16:07:18 +0100856 /* WRITE UNLOCK */
857 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +0100858
Michal Vasko9e036d52016-01-08 10:49:26 +0100859 return 0;
860}
861
Michal Vasko3031aae2016-01-27 16:07:18 +0100862int
863nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +0100864{
865 uint32_t i;
866 int ret = -1;
867
Michal Vasko3031aae2016-01-27 16:07:18 +0100868 /* WRITE LOCK */
869 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +0100870
Michal Vasko3031aae2016-01-27 16:07:18 +0100871 if (!name && !ti) {
872 /* remove all */
Michal Vasko11d142a2016-01-19 15:58:24 +0100873 nc_ctx_lock(-1, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100874 for (i = 0; i < server_opts.endpt_count; ++i) {
875 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko11d142a2016-01-19 15:58:24 +0100876 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
Michal Vasko3031aae2016-01-27 16:07:18 +0100877 close(server_opts.binds[i].sock);
878 pthread_mutex_destroy(&server_opts.endpts[i].endpt_lock);
879 switch (server_opts.binds[i].ti) {
880#ifdef ENABLE_SSH
881 case NC_TI_LIBSSH:
882 nc_server_ssh_opts_clear(server_opts.endpts[i].ti_opts);
883 break;
884#endif
885#ifdef ENABLE_TLS
886 case NC_TI_OPENSSL:
887 nc_server_tls_opts_clear(server_opts.endpts[i].ti_opts);
888 break;
889#endif
890 default:
891 ERRINT;
892 break;
893 }
894 free(server_opts.endpts[i].ti_opts);
Michal Vasko9e036d52016-01-08 10:49:26 +0100895
Michal Vasko9e036d52016-01-08 10:49:26 +0100896 ret = 0;
897 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100898 nc_ctx_unlock();
Michal Vasko3031aae2016-01-27 16:07:18 +0100899 free(server_opts.endpts);
900 server_opts.endpts = NULL;
901 server_opts.endpt_count = 0;
902
Michal Vasko1a38c862016-01-15 15:50:07 +0100903 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +0100904 /* remove one name endpoint or all ti endpoints */
905 for (i = 0; i < server_opts.endpt_count; ++i) {
906 if ((server_opts.binds[i].ti == ti) &&
907 (!name || !strcmp(server_opts.endpts[i].name, name))) {
908
Michal Vasko11d142a2016-01-19 15:58:24 +0100909 nc_ctx_lock(-1, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100910 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko11d142a2016-01-19 15:58:24 +0100911 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
912 nc_ctx_unlock();
Michal Vasko3031aae2016-01-27 16:07:18 +0100913 close(server_opts.binds[i].sock);
914 pthread_mutex_destroy(&server_opts.endpts[i].endpt_lock);
915 switch (server_opts.binds[i].ti) {
916#ifdef ENABLE_SSH
917 case NC_TI_LIBSSH:
918 nc_server_ssh_opts_clear(server_opts.endpts[i].ti_opts);
919 break;
920#endif
921#ifdef ENABLE_TLS
922 case NC_TI_OPENSSL:
923 nc_server_tls_opts_clear(server_opts.endpts[i].ti_opts);
924 break;
925#endif
926 default:
927 ERRINT;
928 break;
929 }
930 free(server_opts.endpts[i].ti_opts);
Michal Vasko1a38c862016-01-15 15:50:07 +0100931
Michal Vasko3031aae2016-01-27 16:07:18 +0100932 --server_opts.endpt_count;
933 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
934 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vasko1a38c862016-01-15 15:50:07 +0100935
936 ret = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +0100937
938 if (name) {
939 /* one name endpoint removed, they are unique, we're done */
940 break;
941 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100942 }
943 }
Michal Vasko9e036d52016-01-08 10:49:26 +0100944 }
945
Michal Vasko3031aae2016-01-27 16:07:18 +0100946 /* WRITE UNLOCK */
947 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +0100948
Michal Vasko9e036d52016-01-08 10:49:26 +0100949 return ret;
950}
951
Michal Vasko1a38c862016-01-15 15:50:07 +0100952API int
953nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +0100954{
Michal Vasko1a38c862016-01-15 15:50:07 +0100955 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +0100956 char *host = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +0100957 uint16_t port, idx;
Michal Vasko9e036d52016-01-08 10:49:26 +0100958
Michal Vasko3031aae2016-01-27 16:07:18 +0100959 if (!server_opts.ctx || !server_opts.endpt_count || !session) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100960 ERRARG;
Michal Vasko1a38c862016-01-15 15:50:07 +0100961 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +0100962 }
963
Michal Vasko3031aae2016-01-27 16:07:18 +0100964 /* WRITE LOCK */
965 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +0100966
Michal Vasko3031aae2016-01-27 16:07:18 +0100967 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &idx);
Michal Vaskob48aa812016-01-18 14:13:09 +0100968
Michal Vasko11d142a2016-01-19 15:58:24 +0100969 if (ret < 0) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100970 /* WRITE UNLOCK */
971 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +0100972 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +0100973 }
Michal Vaskob48aa812016-01-18 14:13:09 +0100974 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +0100975
Michal Vasko1a38c862016-01-15 15:50:07 +0100976 *session = calloc(1, sizeof **session);
Michal Vasko686aa312016-01-21 15:58:18 +0100977 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100978 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100979 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +0100980 free(host);
Michal Vasko3031aae2016-01-27 16:07:18 +0100981 ret = -1;
982 goto fail;
Michal Vasko9e036d52016-01-08 10:49:26 +0100983 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100984 (*session)->status = NC_STATUS_STARTING;
985 (*session)->side = NC_SERVER;
986 (*session)->ctx = server_opts.ctx;
987 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko11d142a2016-01-19 15:58:24 +0100988 nc_ctx_lock(-1, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +0100989 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
Michal Vasko11d142a2016-01-19 15:58:24 +0100990 nc_ctx_unlock();
Michal Vasko1a38c862016-01-15 15:50:07 +0100991 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +0100992
993 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +0100994 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
995 if (!(*session)->ti_lock) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100996 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100997 close(sock);
Michal Vasko1a38c862016-01-15 15:50:07 +0100998 ret = -1;
Michal Vasko9e036d52016-01-08 10:49:26 +0100999 goto fail;
1000 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001001 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001002
Michal Vasko3031aae2016-01-27 16:07:18 +01001003 (*session)->ti_opts = server_opts.endpts[idx].ti_opts;
1004
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001005 /* sock gets assigned to session or closed */
Michal Vasko3031aae2016-01-27 16:07:18 +01001006 if (server_opts.binds[idx].ti == NC_TI_LIBSSH) {
1007 ret = nc_accept_ssh_session(*session, sock, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001008 if (ret < 1) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001009 goto fail;
1010 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001011 } else if (server_opts.binds[idx].ti == NC_TI_OPENSSL) {
1012 ret = nc_accept_tls_session(*session, sock, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001013 if (ret < 1) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001014 goto fail;
1015 }
1016 } else {
1017 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001018 close(sock);
Michal Vasko1a38c862016-01-15 15:50:07 +01001019 ret = -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001020 goto fail;
1021 }
1022
Michal Vasko3031aae2016-01-27 16:07:18 +01001023 /* WRITE UNLOCK */
1024 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1025
Michal Vaskob48aa812016-01-18 14:13:09 +01001026 /* assign new SID atomically */
1027 /* LOCK */
1028 pthread_spin_lock(&server_opts.sid_lock);
1029 (*session)->id = server_opts.new_session_id++;
1030 /* UNLOCK */
1031 pthread_spin_unlock(&server_opts.sid_lock);
1032
Michal Vasko9e036d52016-01-08 10:49:26 +01001033 /* NETCONF handshake */
Michal Vasko1a38c862016-01-15 15:50:07 +01001034 if (nc_handshake(*session)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001035 nc_session_free(*session);
1036 *session = NULL;
1037 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001038 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001039 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01001040
Michal Vasko1a38c862016-01-15 15:50:07 +01001041 return 1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001042
1043fail:
Michal Vasko3031aae2016-01-27 16:07:18 +01001044 /* WRITE UNLOCK */
1045 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1046
Michal Vasko1a38c862016-01-15 15:50:07 +01001047 nc_session_free(*session);
1048 *session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001049 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001050}
1051
Michal Vasko3031aae2016-01-27 16:07:18 +01001052int
Michal Vaskob05053d2016-01-22 16:12:06 +01001053nc_connect_callhome(const char *host, uint16_t port, NC_TRANSPORT_IMPL ti, int timeout, struct nc_session **session)
1054{
1055 int sock, ret;
1056
Michal Vaskoc61c4492016-01-25 11:13:34 +01001057 if (!host || !port || !ti || !session) {
1058 ERRARG;
1059 return -1;
1060 }
1061
Michal Vaskob05053d2016-01-22 16:12:06 +01001062 sock = nc_sock_connect(host, port);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001063 if (sock < 0) {
1064 return -1;
Michal Vaskob05053d2016-01-22 16:12:06 +01001065 }
1066
1067 *session = calloc(1, sizeof **session);
1068 if (!(*session)) {
1069 ERRMEM;
1070 close(sock);
1071 return -1;
1072 }
1073 (*session)->status = NC_STATUS_STARTING;
1074 (*session)->side = NC_SERVER;
1075 (*session)->ctx = server_opts.ctx;
1076 (*session)->flags = NC_SESSION_SHAREDCTX | NC_SESSION_CALLHOME;
1077 nc_ctx_lock(-1, NULL);
1078 (*session)->host = lydict_insert(server_opts.ctx, host, 0);
1079 nc_ctx_unlock();
1080 (*session)->port = port;
1081
1082 /* transport lock */
1083 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1084 if (!(*session)->ti_lock) {
1085 ERRMEM;
1086 close(sock);
1087 ret = -1;
1088 goto fail;
1089 }
1090 pthread_mutex_init((*session)->ti_lock, NULL);
1091
1092 /* sock gets assigned to session or closed */
1093 if (ti == NC_TI_LIBSSH) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001094 (*session)->ti_opts = &ssh_ch_opts;
1095 ret = nc_accept_ssh_session(*session, sock, timeout);
Michal Vaskob05053d2016-01-22 16:12:06 +01001096 if (ret < 1) {
1097 goto fail;
1098 }
1099 } else if (ti == NC_TI_OPENSSL) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001100 (*session)->ti_opts = &tls_ch_opts;
1101 ret = nc_accept_tls_session(*session, sock, timeout);
Michal Vaskob05053d2016-01-22 16:12:06 +01001102 if (ret < 1) {
1103 goto fail;
1104 }
1105 } else {
1106 ERRINT;
1107 close(sock);
1108 ret = -1;
1109 goto fail;
1110 }
1111
1112 /* assign new SID atomically */
1113 /* LOCK */
1114 pthread_spin_lock(&server_opts.sid_lock);
1115 (*session)->id = server_opts.new_session_id++;
1116 /* UNLOCK */
1117 pthread_spin_unlock(&server_opts.sid_lock);
1118
1119 /* NETCONF handshake */
1120 if (nc_handshake(*session)) {
1121 ret = -1;
1122 goto fail;
1123 }
1124 (*session)->status = NC_STATUS_RUNNING;
1125
1126 return 1;
1127
1128fail:
1129 nc_session_free(*session);
1130 *session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001131 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +01001132}
1133
Michal Vasko9e036d52016-01-08 10:49:26 +01001134#endif /* ENABLE_SSH || ENABLE_TLS */