blob: bdd44308ebc850c28082e14a35f7909b0c16a52f [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 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010013 */
14
15#include <stdint.h>
16#include <stdlib.h>
17#include <errno.h>
18#include <string.h>
19#include <poll.h>
20#include <sys/types.h>
21#include <sys/socket.h>
22#include <netinet/in.h>
23#include <arpa/inet.h>
24#include <unistd.h>
Michal Vasko0190bc32016-03-02 15:47:49 +010025#include <fcntl.h>
Michal Vaskob48aa812016-01-18 14:13:09 +010026#include <pthread.h>
Michal Vasko11d142a2016-01-19 15:58:24 +010027#include <time.h>
Michal Vasko086311b2016-01-08 09:53:11 +010028
Michal Vasko1a38c862016-01-15 15:50:07 +010029#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010030#include "session_server.h"
31
Michal Vaskob48aa812016-01-18 14:13:09 +010032struct nc_server_opts server_opts = {
Michal Vasko3031aae2016-01-27 16:07:18 +010033 .endpt_array_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010034};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010035
fanchanghu966f2de2016-07-21 02:28:57 -040036static nc_rpc_clb global_rpc_clb = NULL;
37
Michal Vasko3031aae2016-01-27 16:07:18 +010038extern struct nc_server_ssh_opts ssh_ch_opts;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010039extern pthread_mutex_t ssh_ch_opts_lock;
40
Michal Vasko3031aae2016-01-27 16:07:18 +010041extern struct nc_server_tls_opts tls_ch_opts;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010042extern pthread_mutex_t tls_ch_opts_lock;
Michal Vasko3031aae2016-01-27 16:07:18 +010043
44struct nc_endpt *
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010045nc_server_endpt_lock(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko3031aae2016-01-27 16:07:18 +010046{
47 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010048 struct nc_endpt *endpt = NULL;
49
50 /* READ LOCK */
51 pthread_rwlock_rdlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010052
53 for (i = 0; i < server_opts.endpt_count; ++i) {
54 if ((server_opts.binds[i].ti == ti) && !strcmp(server_opts.endpts[i].name, name)) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010055 endpt = &server_opts.endpts[i];
56 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010057 }
58 }
59
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010060 if (!endpt) {
61 ERR("Endpoint \"%s\" was not found.", name);
62 /* READ UNLOCK */
63 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
64 return NULL;
65 }
66
67 /* ENDPT LOCK */
68 pthread_mutex_lock(&endpt->endpt_lock);
69
70 return endpt;
71}
72
73void
74nc_server_endpt_unlock(struct nc_endpt *endpt)
75{
76 /* ENDPT UNLOCK */
77 pthread_mutex_unlock(&endpt->endpt_lock);
78
79 /* READ UNLOCK */
Michal Vasko27562ad2016-02-02 15:50:39 +010080 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010081}
Michal Vasko086311b2016-01-08 09:53:11 +010082
Michal Vasko1a38c862016-01-15 15:50:07 +010083API void
84nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
85{
Michal Vasko45e53ae2016-04-07 11:46:03 +020086 if (!session) {
87 ERRARG("session");
88 return;
89 } else if (!reason) {
90 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +010091 return;
92 }
93
94 session->term_reason = reason;
95}
96
Michal Vasko086311b2016-01-08 09:53:11 +010097int
Michal Vaskof05562c2016-01-20 12:06:43 +010098nc_sock_listen(const char *address, uint16_t port)
Michal Vasko086311b2016-01-08 09:53:11 +010099{
100 const int optVal = 1;
101 const socklen_t optLen = sizeof(optVal);
102 int is_ipv4, sock;
103 struct sockaddr_storage saddr;
104
105 struct sockaddr_in *saddr4;
106 struct sockaddr_in6 *saddr6;
107
108
109 if (!strchr(address, ':')) {
110 is_ipv4 = 1;
111 } else {
112 is_ipv4 = 0;
113 }
114
115 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
116 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100117 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100118 goto fail;
119 }
120
121 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&optVal, optLen)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100122 ERR("Could not set socket SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100123 goto fail;
124 }
125
126 bzero(&saddr, sizeof(struct sockaddr_storage));
127 if (is_ipv4) {
128 saddr4 = (struct sockaddr_in *)&saddr;
129
130 saddr4->sin_family = AF_INET;
131 saddr4->sin_port = htons(port);
132
133 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100134 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100135 goto fail;
136 }
137
138 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100139 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100140 goto fail;
141 }
142
143 } else {
144 saddr6 = (struct sockaddr_in6 *)&saddr;
145
146 saddr6->sin6_family = AF_INET6;
147 saddr6->sin6_port = htons(port);
148
149 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100150 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100151 goto fail;
152 }
153
154 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100155 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100156 goto fail;
157 }
158 }
159
Michal Vaskofb89d772016-01-08 12:25:35 +0100160 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100161 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100162 goto fail;
163 }
164
165 return sock;
166
167fail:
168 if (sock > -1) {
169 close(sock);
170 }
171
172 return -1;
173}
174
175int
Michal Vasko3031aae2016-01-27 16:07:18 +0100176nc_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 +0100177{
178 uint16_t i;
179 struct pollfd *pfd;
180 struct sockaddr_storage saddr;
181 socklen_t saddr_len = sizeof(saddr);
Michal Vasko0190bc32016-03-02 15:47:49 +0100182 int ret, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100183
184 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100185 if (!pfd) {
186 ERRMEM;
187 return -1;
188 }
189
Michal Vasko086311b2016-01-08 09:53:11 +0100190 for (i = 0; i < bind_count; ++i) {
191 pfd[i].fd = binds[i].sock;
192 pfd[i].events = POLLIN;
193 pfd[i].revents = 0;
194 }
195
196 /* poll for a new connection */
197 errno = 0;
198 ret = poll(pfd, bind_count, timeout);
199 if (!ret) {
200 /* we timeouted */
201 free(pfd);
202 return 0;
203 } else if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100204 ERR("Poll failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100205 free(pfd);
206 return -1;
207 }
208
209 for (i = 0; i < bind_count; ++i) {
210 if (pfd[i].revents & POLLIN) {
211 sock = pfd[i].fd;
212 break;
213 }
214 }
215 free(pfd);
216
217 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100218 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100219 return -1;
220 }
221
222 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100223 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100224 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100225 return -1;
226 }
227
Michal Vasko0190bc32016-03-02 15:47:49 +0100228 /* make the socket non-blocking */
229 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
230 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100231 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100232 return -1;
233 }
234
Michal Vasko3031aae2016-01-27 16:07:18 +0100235 if (idx) {
236 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100237 }
238
Michal Vasko086311b2016-01-08 09:53:11 +0100239 /* host was requested */
240 if (host) {
241 if (saddr.ss_family == AF_INET) {
242 *host = malloc(15);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100243 if (*host) {
244 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, 15)) {
245 ERR("inet_ntop failed (%s).", strerror(errno));
246 free(*host);
247 *host = NULL;
248 }
Michal Vasko086311b2016-01-08 09:53:11 +0100249
Michal Vasko4eb3c312016-03-01 14:09:37 +0100250 if (port) {
251 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
252 }
253 } else {
254 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100255 }
256 } else if (saddr.ss_family == AF_INET6) {
257 *host = malloc(40);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100258 if (*host) {
259 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, 40)) {
260 ERR("inet_ntop failed (%s).", strerror(errno));
261 free(*host);
262 *host = NULL;
263 }
Michal Vasko086311b2016-01-08 09:53:11 +0100264
Michal Vasko4eb3c312016-03-01 14:09:37 +0100265 if (port) {
266 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
267 }
268 } else {
269 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100270 }
271 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100272 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100273 }
274 }
275
276 return ret;
277}
278
Michal Vasko05ba9df2016-01-13 14:40:27 +0100279static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100280nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100281{
282 const char *identifier = NULL, *version = NULL, *format = NULL;
283 char *model_data = NULL;
284 const struct lys_module *module;
285 struct nc_server_error *err;
286 struct lyd_node *child, *data = NULL;
Michal Vasko11d142a2016-01-19 15:58:24 +0100287 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100288
289 LY_TREE_FOR(rpc->child, child) {
290 if (!strcmp(child->schema->name, "identifier")) {
291 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
292 } else if (!strcmp(child->schema->name, "version")) {
293 version = ((struct lyd_node_leaf_list *)child)->value_str;
294 } else if (!strcmp(child->schema->name, "format")) {
295 format = ((struct lyd_node_leaf_list *)child)->value_str;
296 }
297 }
298
299 /* check version */
300 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100301 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
302 nc_err_set_msg(err, "The requested version is not supported.", "en");
303 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100304 }
305
306 /* check and get module with the name identifier */
307 module = ly_ctx_get_module(server_opts.ctx, identifier, version);
308 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200309 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
310 }
311 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100312 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
313 nc_err_set_msg(err, "The requested schema was not found.", "en");
314 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100315 }
316
317 /* check format */
318 if (!format || !strcmp(format, "yang")) {
319 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL);
320 } else if (!strcmp(format, "yin")) {
321 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL);
322 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100323 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
324 nc_err_set_msg(err, "The requested format is not supported.", "en");
325 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100326 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200327 if (!model_data) {
328 ERRINT;
329 return NULL;
330 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100331
Michal Vasko303245c2016-03-24 15:20:03 +0100332 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/output/data");
Michal Vaskod91f6e62016-04-05 11:34:22 +0200333 if (!sdata) {
334 ERRINT;
335 free(model_data);
336 return NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100337 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200338
Michal Vaskob2583f12016-05-12 11:40:23 +0200339 data = lyd_new_path(NULL, server_opts.ctx, "/ietf-netconf-monitoring:get-schema/data", model_data, LYD_PATH_OPT_OUTPUT);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100340 if (!data) {
341 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200342 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100343 return NULL;
344 }
Michal Vaskob2583f12016-05-12 11:40:23 +0200345 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100346
347 return nc_server_reply_data(data, NC_PARAMTYPE_FREE);
348}
349
350static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100351nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100352{
Michal Vasko428087d2016-01-14 16:04:28 +0100353 session->term_reason = NC_SESSION_TERM_CLOSED;
354 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100355}
356
Michal Vasko086311b2016-01-08 09:53:11 +0100357API int
358nc_server_init(struct ly_ctx *ctx)
359{
Michal Vasko05ba9df2016-01-13 14:40:27 +0100360 const struct lys_node *rpc;
361
Michal Vasko086311b2016-01-08 09:53:11 +0100362 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200363 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100364 return -1;
365 }
366
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100367 nc_init();
368
Michal Vasko05ba9df2016-01-13 14:40:27 +0100369 /* set default <get-schema> callback if not specified */
Michal Vasko303245c2016-03-24 15:20:03 +0100370 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema");
Michal Vaskofd100c92016-03-01 15:23:46 +0100371 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100372 lys_set_private(rpc, nc_clb_default_get_schema);
373 }
374
375 /* set default <close-session> callback if not specififed */
Michal Vasko303245c2016-03-24 15:20:03 +0100376 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session");
Michal Vaskofd100c92016-03-01 15:23:46 +0100377 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100378 lys_set_private(rpc, nc_clb_default_close_session);
379 }
380
Michal Vasko086311b2016-01-08 09:53:11 +0100381 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100382
383 server_opts.new_session_id = 1;
384 pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
385
Michal Vasko086311b2016-01-08 09:53:11 +0100386 return 0;
387}
388
Michal Vaskob48aa812016-01-18 14:13:09 +0100389API void
390nc_server_destroy(void)
391{
392 pthread_spin_destroy(&server_opts.sid_lock);
393
Radek Krejci53691be2016-02-22 13:58:37 +0100394#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko3031aae2016-01-27 16:07:18 +0100395 nc_server_del_endpt(NULL, 0);
Michal Vaskob48aa812016-01-18 14:13:09 +0100396#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100397 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100398}
399
Michal Vasko086311b2016-01-08 09:53:11 +0100400API int
401nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
402{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200403 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
404 ERRARG("basic_mode");
405 return -1;
406 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
407 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100408 return -1;
409 }
410
411 server_opts.wd_basic_mode = basic_mode;
412 server_opts.wd_also_supported = also_supported;
413 return 0;
414}
415
Michal Vasko1a38c862016-01-15 15:50:07 +0100416API void
Michal Vasko55f03972016-04-13 08:56:01 +0200417nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
418{
419 if (!basic_mode && !also_supported) {
420 ERRARG("basic_mode and also_supported");
421 return;
422 }
423
424 if (basic_mode) {
425 *basic_mode = server_opts.wd_basic_mode;
426 }
427 if (also_supported) {
428 *also_supported = server_opts.wd_also_supported;
429 }
430}
431
432API void
Michal Vasko086311b2016-01-08 09:53:11 +0100433nc_server_set_capab_interleave(int interleave_support)
434{
435 if (interleave_support) {
436 server_opts.interleave_capab = 1;
437 } else {
438 server_opts.interleave_capab = 0;
439 }
Michal Vasko086311b2016-01-08 09:53:11 +0100440}
441
Michal Vasko55f03972016-04-13 08:56:01 +0200442API int
443nc_server_get_capab_interleave(void)
444{
445 return server_opts.interleave_capab;
446}
447
Michal Vasko1a38c862016-01-15 15:50:07 +0100448API void
Michal Vasko086311b2016-01-08 09:53:11 +0100449nc_server_set_hello_timeout(uint16_t hello_timeout)
450{
Michal Vasko086311b2016-01-08 09:53:11 +0100451 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100452}
453
Michal Vasko55f03972016-04-13 08:56:01 +0200454API uint16_t
455nc_server_get_hello_timeout(void)
456{
457 return server_opts.hello_timeout;
458}
459
Michal Vasko1a38c862016-01-15 15:50:07 +0100460API void
Michal Vasko086311b2016-01-08 09:53:11 +0100461nc_server_set_idle_timeout(uint16_t idle_timeout)
462{
Michal Vasko086311b2016-01-08 09:53:11 +0100463 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100464}
465
Michal Vasko55f03972016-04-13 08:56:01 +0200466API uint16_t
467nc_server_get_idle_timeout(void)
468{
469 return server_opts.idle_timeout;
470}
471
Michal Vasko71090fc2016-05-24 16:37:28 +0200472API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100473nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100474{
Michal Vasko71090fc2016-05-24 16:37:28 +0200475 NC_MSG_TYPE msgtype;
476
Michal Vasko45e53ae2016-04-07 11:46:03 +0200477 if (!server_opts.ctx) {
478 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200479 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200480 } else if (fdin < 0) {
481 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200482 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200483 } else if (fdout < 0) {
484 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200485 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200486 } else if (!username) {
487 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200488 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200489 } else if (!session) {
490 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200491 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100492 }
493
494 /* prepare session structure */
Michal Vasko1a38c862016-01-15 15:50:07 +0100495 *session = calloc(1, sizeof **session);
496 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100497 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200498 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100499 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100500 (*session)->status = NC_STATUS_STARTING;
501 (*session)->side = NC_SERVER;
Michal Vasko086311b2016-01-08 09:53:11 +0100502
503 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100504 (*session)->ti_type = NC_TI_FD;
505 (*session)->ti.fd.in = fdin;
506 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100507
508 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100509 (*session)->flags = NC_SESSION_SHAREDCTX;
510 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100511
Michal Vaskob48aa812016-01-18 14:13:09 +0100512 /* assign new SID atomically */
513 pthread_spin_lock(&server_opts.sid_lock);
514 (*session)->id = server_opts.new_session_id++;
515 pthread_spin_unlock(&server_opts.sid_lock);
516
Michal Vasko086311b2016-01-08 09:53:11 +0100517 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200518 msgtype = nc_handshake(*session);
519 if (msgtype != NC_MSG_HELLO) {
520 nc_session_free(*session, NULL);
521 *session = NULL;
522 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100523 }
Michal Vaskof8352352016-05-24 09:11:36 +0200524 (*session)->session_start = (*session)->last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +0100525 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100526
Michal Vasko71090fc2016-05-24 16:37:28 +0200527 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100528}
Michal Vasko9e036d52016-01-08 10:49:26 +0100529
Michal Vaskob30b99c2016-07-26 11:35:43 +0200530static void
531nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
532{
533 uint8_t i, found = 0;
534
535 for (i = 0; i < ps->queue_len; ++i) {
536 /* idx round buffer adjust */
537 if (ps->queue_begin + i == NC_PS_QUEUE_SIZE) {
538 i = -ps->queue_begin;
539 }
540
541 if (found) {
542 /* move the value back one place */
543 if (ps->queue[ps->queue_begin + i] == id) {
544 /* another equal value, simply cannot be */
545 ERRINT;
546 }
547
548 if (ps->queue_begin + i == 0) {
549 ps->queue[NC_PS_QUEUE_SIZE - 1] = ps->queue[ps->queue_begin + i];
550 } else {
551 ps->queue[ps->queue_begin + i - 1] = ps->queue[ps->queue_begin + i];
552 }
553 } else if (ps->queue[ps->queue_begin + i] == id) {
554 /* found our id, there can be no more equal valid values */
555 found = 1;
556 }
557 }
558
559 if (!found) {
560 ERRINT;
561 }
562 --ps->queue_len;
563}
564
Michal Vaskof04a52a2016-04-07 10:52:10 +0200565int
Michal Vasko26043172016-07-26 14:08:59 +0200566nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200567{
568 int ret;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200569 uint8_t queue_last;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200570 struct timespec ts;
571
Radek Krejci7ac16052016-07-15 11:48:18 +0200572 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200573 ts.tv_sec += NC_READ_TIMEOUT;
574
575 /* LOCK */
576 ret = pthread_mutex_timedlock(&ps->lock, &ts);
577 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200578 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200579 return -1;
580 }
581
582 /* get a unique queue value (by adding 1 to the last added value, if any) */
583 if (ps->queue_len) {
584 queue_last = ps->queue_begin + ps->queue_len - 1;
585 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
586 queue_last -= NC_PS_QUEUE_SIZE;
587 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200588 *id = ps->queue[queue_last] + 1;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200589 } else {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200590 *id = 0;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200591 }
592
593 /* add ourselves into the queue */
594 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko26043172016-07-26 14:08:59 +0200595 ERR("%s: pollsession queue too small.", func);
Michal Vasko0ea456b2016-07-26 12:23:24 +0200596 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200597 return -1;
598 }
599 ++ps->queue_len;
600 queue_last = ps->queue_begin + ps->queue_len - 1;
601 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
602 queue_last -= NC_PS_QUEUE_SIZE;
603 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200604 ps->queue[queue_last] = *id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200605
606 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200607 while (ps->queue[ps->queue_begin] != *id) {
Radek Krejci7ac16052016-07-15 11:48:18 +0200608 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200609 ts.tv_sec += NC_READ_TIMEOUT;
610
611 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
612 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200613 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200614 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200615 nc_ps_queue_remove_id(ps, *id);
616 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200617 return -1;
618 }
619 }
620
Michal Vaskobe86fe32016-04-07 10:43:03 +0200621 /* UNLOCK */
622 pthread_mutex_unlock(&ps->lock);
623
624 return 0;
625}
626
Michal Vaskof04a52a2016-04-07 10:52:10 +0200627int
Michal Vasko26043172016-07-26 14:08:59 +0200628nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200629{
630 int ret;
631 struct timespec ts;
632
Radek Krejci7ac16052016-07-15 11:48:18 +0200633 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200634 ts.tv_sec += NC_READ_TIMEOUT;
635
636 /* LOCK */
637 ret = pthread_mutex_timedlock(&ps->lock, &ts);
638 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200639 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200640 ret = -1;
641 }
642
Michal Vaskob30b99c2016-07-26 11:35:43 +0200643 /* we must be the first, it was our turn after all, right? */
644 if (ps->queue[ps->queue_begin] != id) {
645 ERRINT;
646 return -1;
647 }
648
Michal Vaskobe86fe32016-04-07 10:43:03 +0200649 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200650 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200651
652 /* broadcast to all other threads that the queue moved */
653 pthread_cond_broadcast(&ps->cond);
654
Michal Vaskobe86fe32016-04-07 10:43:03 +0200655 /* UNLOCK */
656 if (!ret) {
657 pthread_mutex_unlock(&ps->lock);
658 }
659
660 return ret;
661}
662
Michal Vasko428087d2016-01-14 16:04:28 +0100663API struct nc_pollsession *
664nc_ps_new(void)
665{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100666 struct nc_pollsession *ps;
667
668 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100669 if (!ps) {
670 ERRMEM;
671 return NULL;
672 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200673 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100674 pthread_mutex_init(&ps->lock, NULL);
675
676 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100677}
678
679API void
680nc_ps_free(struct nc_pollsession *ps)
681{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100682 if (!ps) {
683 return;
684 }
685
Michal Vaskobe86fe32016-04-07 10:43:03 +0200686 if (ps->queue_len) {
687 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
688 }
689
Michal Vasko3a715132016-01-21 15:40:31 +0100690 free(ps->pfds);
Michal Vasko428087d2016-01-14 16:04:28 +0100691 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100692 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200693 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100694
Michal Vasko428087d2016-01-14 16:04:28 +0100695 free(ps);
696}
697
698API int
699nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
700{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200701 uint8_t q_id;
702
Michal Vasko45e53ae2016-04-07 11:46:03 +0200703 if (!ps) {
704 ERRARG("ps");
705 return -1;
706 } else if (!session) {
707 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +0100708 return -1;
709 }
710
Michal Vasko48a63ed2016-03-01 09:48:21 +0100711 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200712 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200713 return -1;
714 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100715
Michal Vasko428087d2016-01-14 16:04:28 +0100716 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100717 ps->pfds = nc_realloc(ps->pfds, ps->session_count * sizeof *ps->pfds);
718 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
719 if (!ps->pfds || !ps->sessions) {
720 ERRMEM;
721 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200722 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100723 return -1;
724 }
Michal Vasko428087d2016-01-14 16:04:28 +0100725
726 switch (session->ti_type) {
727 case NC_TI_FD:
Michal Vasko3a715132016-01-21 15:40:31 +0100728 ps->pfds[ps->session_count - 1].fd = session->ti.fd.in;
Michal Vasko428087d2016-01-14 16:04:28 +0100729 break;
730
Radek Krejci53691be2016-02-22 13:58:37 +0100731#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100732 case NC_TI_LIBSSH:
Michal Vasko3a715132016-01-21 15:40:31 +0100733 ps->pfds[ps->session_count - 1].fd = ssh_get_fd(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100734 break;
735#endif
736
Radek Krejci53691be2016-02-22 13:58:37 +0100737#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100738 case NC_TI_OPENSSL:
Michal Vasko3a715132016-01-21 15:40:31 +0100739 ps->pfds[ps->session_count - 1].fd = SSL_get_rfd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100740 break;
741#endif
742
743 default:
744 ERRINT;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100745 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200746 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100747 return -1;
748 }
Michal Vasko3a715132016-01-21 15:40:31 +0100749 ps->pfds[ps->session_count - 1].events = POLLIN;
750 ps->pfds[ps->session_count - 1].revents = 0;
751 ps->sessions[ps->session_count - 1] = session;
Michal Vasko428087d2016-01-14 16:04:28 +0100752
Michal Vasko48a63ed2016-03-01 09:48:21 +0100753 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200754 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100755}
756
Michal Vasko48a63ed2016-03-01 09:48:21 +0100757static int
Radek Krejcid5f978f2016-03-03 13:14:45 +0100758_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +0100759{
760 uint16_t i;
761
Radek Krejcid5f978f2016-03-03 13:14:45 +0100762 if (index >= 0) {
763 i = (uint16_t)index;
764 goto remove;
765 }
Michal Vasko428087d2016-01-14 16:04:28 +0100766 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100767 if (ps->sessions[i] == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +0100768remove:
Michal Vasko428087d2016-01-14 16:04:28 +0100769 --ps->session_count;
Michal Vasko58005732016-02-02 15:50:52 +0100770 if (i < ps->session_count) {
771 ps->sessions[i] = ps->sessions[ps->session_count];
772 memcpy(&ps->pfds[i], &ps->pfds[ps->session_count], sizeof *ps->pfds);
773 } else if (!ps->session_count) {
774 free(ps->sessions);
775 ps->sessions = NULL;
776 free(ps->pfds);
777 ps->pfds = NULL;
778 }
Michal Vasko428087d2016-01-14 16:04:28 +0100779 return 0;
780 }
781 }
782
Michal Vaskof0537d82016-01-29 14:42:38 +0100783 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +0100784}
785
Michal Vasko48a63ed2016-03-01 09:48:21 +0100786API int
787nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
788{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200789 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200790 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100791
Michal Vasko45e53ae2016-04-07 11:46:03 +0200792 if (!ps) {
793 ERRARG("ps");
794 return -1;
795 } else if (!session) {
796 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +0100797 return -1;
798 }
799
800 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200801 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200802 return -1;
803 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100804
Radek Krejcid5f978f2016-03-03 13:14:45 +0100805 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100806
807 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200808 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100809
Michal Vaskobe86fe32016-04-07 10:43:03 +0200810 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100811}
812
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100813API uint16_t
814nc_ps_session_count(struct nc_pollsession *ps)
815{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200816 uint8_t q_id;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100817 uint16_t count;
818
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100819 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200820 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100821 return 0;
822 }
823
Michal Vasko48a63ed2016-03-01 09:48:21 +0100824 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200825 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200826 return -1;
827 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100828
829 count = ps->session_count;
830
831 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200832 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100833
834 return count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100835}
836
Michal Vasko71090fc2016-05-24 16:37:28 +0200837/* must be called holding the session lock!
838 * returns: NC_PSPOLL_ERROR,
839 * NC_PSPOLL_BAD_RPC,
840 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
841 * NC_PSPOLL_RPC
842 */
843static int
Michal Vasko428087d2016-01-14 16:04:28 +0100844nc_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
845{
846 struct lyxml_elem *xml = NULL;
847 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200848 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200849 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100850
Michal Vasko45e53ae2016-04-07 11:46:03 +0200851 if (!session) {
852 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200853 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200854 } else if (!rpc) {
855 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +0200856 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100857 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100858 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200859 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100860 }
861
862 msgtype = nc_read_msg(session, &xml);
863
864 switch (msgtype) {
865 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +0200866 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100867 if (!*rpc) {
868 ERRMEM;
869 goto error;
870 }
Michal Vaskoca4a2422016-02-02 12:17:14 +0100871
Radek Krejcif93c7d42016-04-06 13:41:15 +0200872 ly_errno = LY_SUCCESS;
Michal Vasko428087d2016-01-14 16:04:28 +0100873 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child, LYD_OPT_DESTRUCT | LYD_OPT_RPC);
Michal Vaskoca4a2422016-02-02 12:17:14 +0100874 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +0200875 /* parsing RPC failed */
Radek Krejci877e1822016-04-06 16:37:43 +0200876 reply = nc_server_reply_err(nc_err_libyang());
Radek Krejci844662e2016-04-13 16:54:43 +0200877 ret = nc_write_msg(session, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200878 nc_server_reply_free(reply);
879 if (ret == -1) {
880 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200881 }
Michal Vasko71090fc2016-05-24 16:37:28 +0200882 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
883 } else {
884 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +0100885 }
Michal Vasko428087d2016-01-14 16:04:28 +0100886 (*rpc)->root = xml;
887 break;
888 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +0100889 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200890 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100891 goto error;
892 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +0100893 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200894 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100895 goto error;
896 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +0100897 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200898 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100899 goto error;
900 default:
Michal Vasko71090fc2016-05-24 16:37:28 +0200901 /* NC_MSG_ERROR,
Michal Vasko428087d2016-01-14 16:04:28 +0100902 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
903 */
Michal Vasko71090fc2016-05-24 16:37:28 +0200904 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100905 break;
906 }
907
Michal Vasko71090fc2016-05-24 16:37:28 +0200908 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100909
910error:
911 /* cleanup */
912 lyxml_free(server_opts.ctx, xml);
913
Michal Vasko71090fc2016-05-24 16:37:28 +0200914 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100915}
916
fanchanghu966f2de2016-07-21 02:28:57 -0400917API void
918nc_set_global_rpc_clb(nc_rpc_clb clb)
919{
920 global_rpc_clb = clb;
921}
922
Michal Vasko71090fc2016-05-24 16:37:28 +0200923/* must be called holding the session lock!
924 * returns: NC_PSPOLL_ERROR,
925 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
926 * NC_PSPOLL_REPLY_ERROR,
927 * 0
928 */
929static int
Michal Vasko428087d2016-01-14 16:04:28 +0100930nc_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
931{
932 nc_rpc_clb clb;
933 struct nc_server_reply *reply;
Michal Vasko71090fc2016-05-24 16:37:28 +0200934 int ret = 0, r;
Michal Vasko428087d2016-01-14 16:04:28 +0100935
Michal Vasko4a827e52016-03-03 10:59:00 +0100936 if (!rpc) {
937 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200938 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +0100939 }
940
fanchanghu966f2de2016-07-21 02:28:57 -0400941 if (rpc->tree->schema->priv) {
942 clb = (nc_rpc_clb) rpc->tree->schema->priv;
Michal Vasko428087d2016-01-14 16:04:28 +0100943 reply = clb(rpc->tree, session);
fanchanghu966f2de2016-07-21 02:28:57 -0400944 } else {
945 if (global_rpc_clb) {
946 reply = global_rpc_clb(rpc->tree, session);
947 } else { /* no callback, reply with a not-implemented error */
948 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
949 }
Michal Vasko428087d2016-01-14 16:04:28 +0100950 }
951
952 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100953 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +0100954 }
Michal Vasko71090fc2016-05-24 16:37:28 +0200955 r = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
956 if (reply->type == NC_RPL_ERROR) {
957 ret |= NC_PSPOLL_REPLY_ERROR;
958 }
959 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +0100960
Michal Vasko71090fc2016-05-24 16:37:28 +0200961 if (r == -1) {
962 ERR("Session %u: failed to write reply.", session->id);
963 ret |= NC_PSPOLL_ERROR;
964 }
Michal Vasko428087d2016-01-14 16:04:28 +0100965
966 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
967 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
968 session->status = NC_STATUS_INVALID;
969 }
970
Michal Vasko71090fc2016-05-24 16:37:28 +0200971 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100972}
973
974API int
Michal Vasko71090fc2016-05-24 16:37:28 +0200975nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
Michal Vasko428087d2016-01-14 16:04:28 +0100976{
977 int ret;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200978 uint8_t q_id;
Michal Vasko3512e402016-01-28 16:22:34 +0100979 uint16_t i;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100980 time_t cur_time;
Michal Vasko71090fc2016-05-24 16:37:28 +0200981 struct nc_session *cur_session;
Michal Vasko4a827e52016-03-03 10:59:00 +0100982 struct nc_server_rpc *rpc = NULL;
Michal Vasko428087d2016-01-14 16:04:28 +0100983
984 if (!ps || !ps->session_count) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200985 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +0200986 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100987 }
988
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100989 cur_time = time(NULL);
990
Michal Vasko48a63ed2016-03-01 09:48:21 +0100991 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200992 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +0200993 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200994 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100995
Michal Vasko428087d2016-01-14 16:04:28 +0100996 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100997 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
998 ERR("Session %u: session not running.", ps->sessions[i]->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200999 ret = NC_PSPOLL_ERROR;
1000 if (session) {
1001 *session = ps->sessions[i];
1002 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001003 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001004 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001005
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001006 /* TODO invalidate only sessions without subscription */
Michal Vaskobae4ca32016-07-28 15:47:25 +02001007 if (server_opts.idle_timeout && (cur_time >= ps->sessions[i]->last_rpc + server_opts.idle_timeout)) {
Michal Vasko3a715132016-01-21 15:40:31 +01001008 ERR("Session %u: session idle timeout elapsed.", ps->sessions[i]->id);
1009 ps->sessions[i]->status = NC_STATUS_INVALID;
1010 ps->sessions[i]->term_reason = NC_SESSION_TERM_TIMEOUT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001011 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1012 if (session) {
1013 *session = ps->sessions[i];
1014 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001015 goto finish;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001016 }
1017
Michal Vasko3a715132016-01-21 15:40:31 +01001018 if (ps->pfds[i].revents) {
Michal Vaskobd8ef262016-01-20 11:09:27 +01001019 break;
1020 }
Michal Vasko428087d2016-01-14 16:04:28 +01001021 }
1022
Michal Vaskobd8ef262016-01-20 11:09:27 +01001023 if (i == ps->session_count) {
Radek Krejci53691be2016-02-22 13:58:37 +01001024#ifdef NC_ENABLED_SSH
Michal Vasko3a715132016-01-21 15:40:31 +01001025retry_poll:
Michal Vasko3512e402016-01-28 16:22:34 +01001026#endif
Michal Vaskobd8ef262016-01-20 11:09:27 +01001027 /* no leftover event */
1028 i = 0;
Michal Vasko3a715132016-01-21 15:40:31 +01001029 ret = poll(ps->pfds, ps->session_count, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001030 if (ret < 0) {
1031 ERR("Poll failed (%s).", strerror(errno));
1032 ret = NC_PSPOLL_ERROR;
1033 goto finish;
1034 } else if (!ret) {
1035 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001036 goto finish;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001037 }
Michal Vasko428087d2016-01-14 16:04:28 +01001038 }
1039
Michal Vaskobd8ef262016-01-20 11:09:27 +01001040 /* find the first fd with POLLIN, we don't care if there are more now */
1041 for (; i < ps->session_count; ++i) {
Michal Vasko46eac552016-05-30 15:27:25 +02001042 if (ps->pfds[i].revents & (POLLHUP | POLLNVAL)) {
Michal Vasko3a715132016-01-21 15:40:31 +01001043 ERR("Session %u: communication socket unexpectedly closed.", ps->sessions[i]->id);
1044 ps->sessions[i]->status = NC_STATUS_INVALID;
1045 ps->sessions[i]->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko71090fc2016-05-24 16:37:28 +02001046 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1047 if (session) {
1048 *session = ps->sessions[i];
1049 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001050 goto finish;
Michal Vasko3a715132016-01-21 15:40:31 +01001051 } else if (ps->pfds[i].revents & POLLERR) {
1052 ERR("Session %u: communication socket error.", ps->sessions[i]->id);
1053 ps->sessions[i]->status = NC_STATUS_INVALID;
1054 ps->sessions[i]->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko71090fc2016-05-24 16:37:28 +02001055 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1056 if (session) {
1057 *session = ps->sessions[i];
1058 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001059 goto finish;
Michal Vasko3a715132016-01-21 15:40:31 +01001060 } else if (ps->pfds[i].revents & POLLIN) {
Radek Krejci53691be2016-02-22 13:58:37 +01001061#ifdef NC_ENABLED_SSH
Michal Vasko96164bf2016-01-21 15:41:58 +01001062 if (ps->sessions[i]->ti_type == NC_TI_LIBSSH) {
Michal Vasko3512e402016-01-28 16:22:34 +01001063 uint16_t j;
1064
Michal Vasko96164bf2016-01-21 15:41:58 +01001065 /* things are not that simple with SSH... */
Michal Vasko62be1ce2016-03-03 13:24:52 +01001066 ret = nc_ssh_pollin(ps->sessions[i], timeout);
Michal Vasko96164bf2016-01-21 15:41:58 +01001067
1068 /* clear POLLIN on sessions sharing this session's SSH session */
Michal Vasko71090fc2016-05-24 16:37:28 +02001069 if (ret & (NC_PSPOLL_RPC | NC_PSPOLL_SSH_MSG | NC_PSPOLL_SSH_CHANNEL)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001070 for (j = i + 1; j < ps->session_count; ++j) {
1071 if (ps->pfds[j].fd == ps->pfds[i].fd) {
1072 ps->pfds[j].revents = 0;
1073 }
1074 }
1075 }
1076
Michal Vasko71090fc2016-05-24 16:37:28 +02001077 /* SSH message only */
1078 if (!(ret & (NC_PSPOLL_RPC | NC_PSPOLL_PENDING))) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001079 ps->pfds[i].revents = 0;
Michal Vasko71090fc2016-05-24 16:37:28 +02001080 if (session) {
1081 *session = ps->sessions[i];
1082 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001083 goto finish;
Michal Vasko96164bf2016-01-21 15:41:58 +01001084
1085 /* event occurred on some other channel */
Michal Vasko71090fc2016-05-24 16:37:28 +02001086 } else if (ret & NC_PSPOLL_PENDING) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001087 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001088 if (i == ps->session_count - 1) {
1089 /* last session and it is not the right channel, ... */
Michal Vasko8c748832016-02-03 15:32:16 +01001090 if (!timeout) {
Michal Vasko428087d2016-01-14 16:04:28 +01001091 /* ... timeout is 0, so that is it */
Michal Vasko71090fc2016-05-24 16:37:28 +02001092 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001093 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001094 }
Michal Vasko8c748832016-02-03 15:32:16 +01001095 /* ... retry polling reasonable time apart ... */
1096 usleep(NC_TIMEOUT_STEP);
1097 if (timeout > 0) {
1098 /* ... and decrease timeout, if not -1 */
Michal Vasko7b38e232016-02-26 15:01:07 +01001099 timeout -= NC_TIMEOUT_STEP * 1000;
Michal Vasko8c748832016-02-03 15:32:16 +01001100 }
1101 goto retry_poll;
Michal Vasko428087d2016-01-14 16:04:28 +01001102 }
1103 /* check other sessions */
1104 continue;
Michal Vasko428087d2016-01-14 16:04:28 +01001105 }
1106 }
Radek Krejci53691be2016-02-22 13:58:37 +01001107#endif /* NC_ENABLED_SSH */
Michal Vasko428087d2016-01-14 16:04:28 +01001108
Michal Vaskobd8ef262016-01-20 11:09:27 +01001109 /* we are going to process it now */
Michal Vasko3a715132016-01-21 15:40:31 +01001110 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001111 break;
1112 }
1113 }
1114
1115 if (i == ps->session_count) {
1116 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001117 ret = NC_PSPOLL_ERROR;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001118 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001119 }
1120
1121 /* this is the session with some data available for reading */
Michal Vasko71090fc2016-05-24 16:37:28 +02001122 cur_session = ps->sessions[i];
1123 if (session) {
1124 *session = cur_session;
1125 }
Michal Vasko428087d2016-01-14 16:04:28 +01001126
Michal Vaskobd8ef262016-01-20 11:09:27 +01001127 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal Vasko71090fc2016-05-24 16:37:28 +02001128 ret = nc_timedlock(cur_session->ti_lock, timeout);
1129 if (ret < 0) {
1130 ret = NC_PSPOLL_ERROR;
1131 goto finish;
1132 } else if (!ret) {
1133 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001134 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001135 }
1136
Michal Vasko71090fc2016-05-24 16:37:28 +02001137 ret = nc_recv_rpc(cur_session, &rpc);
1138 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1139 pthread_mutex_unlock(cur_session->ti_lock);
1140 if (cur_session->status != NC_STATUS_RUNNING) {
1141 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001142 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001143 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001144 }
1145
Michal Vasko71090fc2016-05-24 16:37:28 +02001146 cur_session->last_rpc = time(NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001147
Michal Vasko428087d2016-01-14 16:04:28 +01001148 /* process RPC */
Michal Vasko71090fc2016-05-24 16:37:28 +02001149 ret |= nc_send_reply(cur_session, rpc);
Michal Vasko428087d2016-01-14 16:04:28 +01001150
Michal Vasko71090fc2016-05-24 16:37:28 +02001151 pthread_mutex_unlock(cur_session->ti_lock);
1152 if (cur_session->status != NC_STATUS_RUNNING) {
1153 ret |= NC_PSPOLL_SESSION_TERM;
1154 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1155 ret |= NC_PSPOLL_SESSION_ERROR;
1156 }
Michal Vasko428087d2016-01-14 16:04:28 +01001157 }
Radek Krejcif93c7d42016-04-06 13:41:15 +02001158
Michal Vaskoca4a2422016-02-02 12:17:14 +01001159 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vaskobd8ef262016-01-20 11:09:27 +01001160
1161 /* is there some other socket waiting? */
1162 for (++i; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +01001163 if (ps->pfds[i].revents) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001164 ret |= NC_PSPOLL_PENDING;
1165 break;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001166 }
1167 }
1168
Michal Vasko48a63ed2016-03-01 09:48:21 +01001169finish:
1170 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001171 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001172 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001173}
1174
Michal Vaskod09eae62016-02-01 10:32:52 +01001175API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001176nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001177{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001178 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001179 uint16_t i;
1180 struct nc_session *session;
1181
Michal Vasko9a25e932016-02-01 10:36:42 +01001182 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001183 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001184 return;
1185 }
1186
Michal Vasko48a63ed2016-03-01 09:48:21 +01001187 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001188 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001189 return;
1190 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001191
Michal Vasko48a63ed2016-03-01 09:48:21 +01001192 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001193 for (i = 0; i < ps->session_count; i++) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001194 nc_session_free(ps->sessions[i], data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001195 }
1196 free(ps->sessions);
1197 ps->sessions = NULL;
1198 free(ps->pfds);
1199 ps->pfds = NULL;
1200 ps->session_count = 0;
1201 } else {
1202 for (i = 0; i < ps->session_count; ) {
1203 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1204 session = ps->sessions[i];
Radek Krejcid5f978f2016-03-03 13:14:45 +01001205 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001206 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001207 continue;
1208 }
1209
1210 ++i;
1211 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001212 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001213
1214 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001215 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001216}
1217
Radek Krejci53691be2016-02-22 13:58:37 +01001218#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001219
Michal Vasko3031aae2016-01-27 16:07:18 +01001220int
1221nc_server_add_endpt_listen(const char *name, const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001222{
1223 int sock;
Michal Vasko3031aae2016-01-27 16:07:18 +01001224 uint16_t i;
Radek Krejci53691be2016-02-22 13:58:37 +01001225#ifdef NC_ENABLED_SSH
Michal Vasko08a629a2016-02-02 12:20:47 +01001226 struct nc_server_ssh_opts *ssh_opts;
1227#endif
Michal Vasko9e036d52016-01-08 10:49:26 +01001228
Michal Vasko45e53ae2016-04-07 11:46:03 +02001229 if (!name) {
1230 ERRARG("name");
1231 return -1;
1232 } else if (!address) {
1233 ERRARG("address");
1234 return -1;
1235 } else if (!port) {
1236 ERRARG("port");
Michal Vasko9e036d52016-01-08 10:49:26 +01001237 return -1;
1238 }
1239
Michal Vasko51e514d2016-02-02 15:51:52 +01001240 /* WRITE LOCK */
1241 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001242
1243 /* check name uniqueness */
1244 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskod4c03a82016-02-08 15:27:26 +01001245 if ((server_opts.binds[i].ti == ti) && !strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001246 ERR("Endpoint \"%s\" already exists.", name);
Michal Vasko51e514d2016-02-02 15:51:52 +01001247 /* WRITE UNLOCK */
Michal Vasko9faf1c82016-02-01 13:26:19 +01001248 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001249 return -1;
1250 }
1251 }
1252
Michal Vasko9e036d52016-01-08 10:49:26 +01001253 sock = nc_sock_listen(address, port);
1254 if (sock == -1) {
Michal Vasko51e514d2016-02-02 15:51:52 +01001255 /* WRITE UNLOCK */
1256 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001257 return -1;
1258 }
1259
Michal Vasko3031aae2016-01-27 16:07:18 +01001260 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001261 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
1262 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
1263 if (!server_opts.binds || !server_opts.endpts) {
1264 ERRMEM;
1265 /* WRITE UNLOCK */
1266 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko0f74da52016-03-03 08:52:52 +01001267 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001268 return -1;
1269 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001270
Michal Vasko3031aae2016-01-27 16:07:18 +01001271 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
1272 server_opts.binds[server_opts.endpt_count - 1].address = lydict_insert(server_opts.ctx, address, 0);
Michal Vasko3031aae2016-01-27 16:07:18 +01001273 server_opts.binds[server_opts.endpt_count - 1].port = port;
1274 server_opts.binds[server_opts.endpt_count - 1].sock = sock;
1275 server_opts.binds[server_opts.endpt_count - 1].ti = ti;
1276 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001277#ifdef NC_ENABLED_SSH
Michal Vasko3031aae2016-01-27 16:07:18 +01001278 case NC_TI_LIBSSH:
Michal Vasko08a629a2016-02-02 12:20:47 +01001279 ssh_opts = calloc(1, sizeof *ssh_opts);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001280 if (!ssh_opts) {
1281 ERRMEM;
1282 /* WRITE UNLOCK */
1283 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1284 return -1;
1285 }
Michal Vasko08a629a2016-02-02 12:20:47 +01001286 /* set default values */
1287 ssh_opts->auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1288 ssh_opts->auth_attempts = 3;
1289 ssh_opts->auth_timeout = 10;
1290
1291 server_opts.endpts[server_opts.endpt_count - 1].ti_opts = ssh_opts;
Michal Vasko3031aae2016-01-27 16:07:18 +01001292 break;
1293#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001294#ifdef NC_ENABLED_TLS
Michal Vasko3031aae2016-01-27 16:07:18 +01001295 case NC_TI_OPENSSL:
1296 server_opts.endpts[server_opts.endpt_count - 1].ti_opts = calloc(1, sizeof(struct nc_server_tls_opts));
Michal Vasko4eb3c312016-03-01 14:09:37 +01001297 if (!server_opts.endpts[server_opts.endpt_count - 1].ti_opts) {
1298 ERRMEM;
1299 /* WRITE UNLOCK */
1300 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1301 return -1;
1302 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001303 break;
1304#endif
1305 default:
1306 ERRINT;
1307 server_opts.endpts[server_opts.endpt_count - 1].ti_opts = NULL;
1308 break;
1309 }
1310 pthread_mutex_init(&server_opts.endpts[server_opts.endpt_count - 1].endpt_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001311
Michal Vasko3031aae2016-01-27 16:07:18 +01001312 /* WRITE UNLOCK */
1313 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001314
Michal Vasko9e036d52016-01-08 10:49:26 +01001315 return 0;
1316}
1317
Michal Vasko3031aae2016-01-27 16:07:18 +01001318int
Michal Vaskoda514772016-02-01 11:32:01 +01001319nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1320{
1321 struct nc_endpt *endpt;
1322 struct nc_bind *bind = NULL;
1323 uint16_t i;
1324 int sock;
1325
Michal Vasko45e53ae2016-04-07 11:46:03 +02001326 if (!endpt_name) {
1327 ERRARG("endpt_name");
1328 return -1;
1329 } else if ((!address && !port) || (address && port)) {
1330 ERRARG("address and port");
1331 return -1;
1332 } else if (!ti) {
1333 ERRARG("ti");
Michal Vaskoda514772016-02-01 11:32:01 +01001334 return -1;
1335 }
1336
Michal Vasko51e514d2016-02-02 15:51:52 +01001337 /* LOCK */
Michal Vaskoda514772016-02-01 11:32:01 +01001338 endpt = nc_server_endpt_lock(endpt_name, ti);
1339 if (!endpt) {
1340 return -1;
1341 }
1342
1343 /* we need to learn the index, to get the bind :-/ */
1344 for (i = 0; i < server_opts.endpt_count; ++i) {
1345 if (&server_opts.endpts[i] == endpt) {
1346 bind = &server_opts.binds[i];
1347 }
1348 }
1349 if (!bind) {
1350 ERRINT;
Michal Vasko51e514d2016-02-02 15:51:52 +01001351 goto fail;
Michal Vaskoda514772016-02-01 11:32:01 +01001352 }
1353
1354 if (address) {
1355 sock = nc_sock_listen(address, bind->port);
1356 } else {
1357 sock = nc_sock_listen(bind->address, port);
1358 }
1359 if (sock == -1) {
Michal Vasko51e514d2016-02-02 15:51:52 +01001360 goto fail;
Michal Vaskoda514772016-02-01 11:32:01 +01001361 }
1362
1363 /* close old socket, update parameters */
1364 close(bind->sock);
1365 bind->sock = sock;
1366 if (address) {
1367 lydict_remove(server_opts.ctx, bind->address);
1368 bind->address = lydict_insert(server_opts.ctx, address, 0);
1369 } else {
1370 bind->port = port;
1371 }
1372
Michal Vasko51e514d2016-02-02 15:51:52 +01001373 /* UNLOCK */
Michal Vasko7a93af72016-02-01 16:00:15 +01001374 nc_server_endpt_unlock(endpt);
Michal Vaskoda514772016-02-01 11:32:01 +01001375 return 0;
Michal Vasko51e514d2016-02-02 15:51:52 +01001376
1377fail:
1378 /* UNLOCK */
1379 nc_server_endpt_unlock(endpt);
1380 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001381}
1382
1383int
Michal Vasko3031aae2016-01-27 16:07:18 +01001384nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001385{
1386 uint32_t i;
1387 int ret = -1;
1388
Michal Vasko3031aae2016-01-27 16:07:18 +01001389 /* WRITE LOCK */
1390 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001391
Michal Vasko3031aae2016-01-27 16:07:18 +01001392 if (!name && !ti) {
1393 /* remove all */
Michal Vasko3031aae2016-01-27 16:07:18 +01001394 for (i = 0; i < server_opts.endpt_count; ++i) {
1395 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko11d142a2016-01-19 15:58:24 +01001396 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
Michal Vasko51e514d2016-02-02 15:51:52 +01001397
Michal Vasko3031aae2016-01-27 16:07:18 +01001398 close(server_opts.binds[i].sock);
1399 pthread_mutex_destroy(&server_opts.endpts[i].endpt_lock);
1400 switch (server_opts.binds[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001401#ifdef NC_ENABLED_SSH
Michal Vasko3031aae2016-01-27 16:07:18 +01001402 case NC_TI_LIBSSH:
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001403 nc_server_ssh_clear_opts(server_opts.endpts[i].ti_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001404 break;
1405#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001406#ifdef NC_ENABLED_TLS
Michal Vasko3031aae2016-01-27 16:07:18 +01001407 case NC_TI_OPENSSL:
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001408 nc_server_tls_clear_opts(server_opts.endpts[i].ti_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001409 break;
1410#endif
1411 default:
1412 ERRINT;
1413 break;
1414 }
1415 free(server_opts.endpts[i].ti_opts);
Michal Vasko9e036d52016-01-08 10:49:26 +01001416
Michal Vasko9e036d52016-01-08 10:49:26 +01001417 ret = 0;
1418 }
Michal Vasko7ddc5702016-02-08 15:29:39 +01001419 free(server_opts.binds);
1420 server_opts.binds = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +01001421 free(server_opts.endpts);
1422 server_opts.endpts = NULL;
1423 server_opts.endpt_count = 0;
1424
Michal Vasko1a38c862016-01-15 15:50:07 +01001425 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +01001426 /* remove one name endpoint or all ti endpoints */
1427 for (i = 0; i < server_opts.endpt_count; ++i) {
1428 if ((server_opts.binds[i].ti == ti) &&
1429 (!name || !strcmp(server_opts.endpts[i].name, name))) {
1430
Michal Vasko3031aae2016-01-27 16:07:18 +01001431 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko11d142a2016-01-19 15:58:24 +01001432 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
Michal Vasko3031aae2016-01-27 16:07:18 +01001433 close(server_opts.binds[i].sock);
1434 pthread_mutex_destroy(&server_opts.endpts[i].endpt_lock);
1435 switch (server_opts.binds[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001436#ifdef NC_ENABLED_SSH
Michal Vasko3031aae2016-01-27 16:07:18 +01001437 case NC_TI_LIBSSH:
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001438 nc_server_ssh_clear_opts(server_opts.endpts[i].ti_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001439 break;
1440#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001441#ifdef NC_ENABLED_TLS
Michal Vasko3031aae2016-01-27 16:07:18 +01001442 case NC_TI_OPENSSL:
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001443 nc_server_tls_clear_opts(server_opts.endpts[i].ti_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001444 break;
1445#endif
1446 default:
1447 ERRINT;
1448 break;
1449 }
1450 free(server_opts.endpts[i].ti_opts);
Michal Vasko1a38c862016-01-15 15:50:07 +01001451
Michal Vasko3031aae2016-01-27 16:07:18 +01001452 --server_opts.endpt_count;
Michal Vaskoc0256492016-02-02 12:19:06 +01001453 if (i < server_opts.endpt_count) {
1454 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1455 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
1456 } else if (!server_opts.endpt_count) {
1457 free(server_opts.binds);
1458 server_opts.binds = NULL;
1459 free(server_opts.endpts);
1460 server_opts.endpts = NULL;
1461 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001462
1463 ret = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001464
1465 if (name) {
1466 /* one name endpoint removed, they are unique, we're done */
1467 break;
1468 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001469 }
1470 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001471 }
1472
Michal Vasko3031aae2016-01-27 16:07:18 +01001473 /* WRITE UNLOCK */
1474 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001475
Michal Vasko9e036d52016-01-08 10:49:26 +01001476 return ret;
1477}
1478
Michal Vasko71090fc2016-05-24 16:37:28 +02001479API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01001480nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001481{
Michal Vasko71090fc2016-05-24 16:37:28 +02001482 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01001483 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01001484 char *host = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +01001485 uint16_t port, idx;
Michal Vasko9e036d52016-01-08 10:49:26 +01001486
Michal Vasko45e53ae2016-04-07 11:46:03 +02001487 if (!server_opts.ctx) {
1488 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001489 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001490 } else if (!session) {
1491 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001492 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001493 }
1494
Michal Vasko51e514d2016-02-02 15:51:52 +01001495 /* we have to hold WRITE for the whole time, since there is not
1496 * a way of downgrading the lock to READ */
1497 /* WRITE LOCK */
1498 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
1499
1500 if (!server_opts.endpt_count) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001501 ERRINIT;
Michal Vasko51e514d2016-02-02 15:51:52 +01001502 /* WRITE UNLOCK */
1503 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001504 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01001505 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001506
Michal Vasko3031aae2016-01-27 16:07:18 +01001507 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &idx);
Michal Vaskob48aa812016-01-18 14:13:09 +01001508
Michal Vasko50456e82016-02-02 12:16:08 +01001509 if (ret < 1) {
Michal Vasko51e514d2016-02-02 15:51:52 +01001510 /* WRITE UNLOCK */
Michal Vasko3031aae2016-01-27 16:07:18 +01001511 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01001512 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02001513 if (!ret) {
1514 return NC_MSG_WOULDBLOCK;
1515 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001516 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001517 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001518 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001519
Michal Vasko1a38c862016-01-15 15:50:07 +01001520 *session = calloc(1, sizeof **session);
Michal Vasko686aa312016-01-21 15:58:18 +01001521 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001522 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001523 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01001524 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02001525 msgtype = NC_MSG_ERROR;
1526 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001527 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001528 (*session)->status = NC_STATUS_STARTING;
1529 (*session)->side = NC_SERVER;
1530 (*session)->ctx = server_opts.ctx;
1531 (*session)->flags = NC_SESSION_SHAREDCTX;
1532 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
1533 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01001534
1535 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +01001536 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1537 if (!(*session)->ti_lock) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001538 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001539 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001540 msgtype = NC_MSG_ERROR;
1541 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001542 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001543 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001544
Michal Vasko2cc4c682016-03-01 09:16:48 +01001545 (*session)->data = server_opts.endpts[idx].ti_opts;
Michal Vasko3031aae2016-01-27 16:07:18 +01001546
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001547 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001548#ifdef NC_ENABLED_SSH
Michal Vasko3031aae2016-01-27 16:07:18 +01001549 if (server_opts.binds[idx].ti == NC_TI_LIBSSH) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001550 ret = nc_accept_ssh_session(*session, sock, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001551 if (ret < 0) {
1552 msgtype = NC_MSG_ERROR;
1553 goto cleanup;
1554 } else if (!ret) {
1555 msgtype = NC_MSG_WOULDBLOCK;
1556 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001557 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001558 } else
1559#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001560#ifdef NC_ENABLED_TLS
Michal Vasko3d865d22016-01-28 16:00:53 +01001561 if (server_opts.binds[idx].ti == NC_TI_OPENSSL) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001562 ret = nc_accept_tls_session(*session, sock, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001563 if (ret < 0) {
1564 msgtype = NC_MSG_ERROR;
1565 goto cleanup;
1566 } else if (!ret) {
1567 msgtype = NC_MSG_WOULDBLOCK;
1568 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001569 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001570 } else
1571#endif
1572 {
Michal Vasko9e036d52016-01-08 10:49:26 +01001573 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001574 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001575 msgtype = NC_MSG_ERROR;
1576 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001577 }
1578
Michal Vasko2cc4c682016-03-01 09:16:48 +01001579 (*session)->data = NULL;
1580
Michal Vasko51e514d2016-02-02 15:51:52 +01001581 /* WRITE UNLOCK */
Michal Vasko3031aae2016-01-27 16:07:18 +01001582 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1583
Michal Vaskob48aa812016-01-18 14:13:09 +01001584 /* assign new SID atomically */
1585 /* LOCK */
1586 pthread_spin_lock(&server_opts.sid_lock);
1587 (*session)->id = server_opts.new_session_id++;
1588 /* UNLOCK */
1589 pthread_spin_unlock(&server_opts.sid_lock);
1590
Michal Vasko9e036d52016-01-08 10:49:26 +01001591 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001592 msgtype = nc_handshake(*session);
1593 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001594 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001595 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001596 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001597 }
Michal Vaskof802fdc2016-07-28 15:47:00 +02001598 (*session)->session_start = (*session)->last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001599 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01001600
Michal Vasko71090fc2016-05-24 16:37:28 +02001601 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001602
Michal Vasko71090fc2016-05-24 16:37:28 +02001603cleanup:
Michal Vasko3031aae2016-01-27 16:07:18 +01001604 /* WRITE UNLOCK */
1605 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1606
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001607 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001608 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001609 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001610}
1611
Michal Vasko71090fc2016-05-24 16:37:28 +02001612NC_MSG_TYPE
Michal Vasko8f5270d2016-02-29 16:22:25 +01001613nc_connect_callhome(const char *host, uint16_t port, NC_TRANSPORT_IMPL ti, struct nc_session **session)
Michal Vaskob05053d2016-01-22 16:12:06 +01001614{
Michal Vasko71090fc2016-05-24 16:37:28 +02001615 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01001616 int sock, ret;
1617
Michal Vasko45e53ae2016-04-07 11:46:03 +02001618 if (!host) {
1619 ERRARG("host");
Michal Vasko71090fc2016-05-24 16:37:28 +02001620 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001621 } else if (!port) {
1622 ERRARG("port");
Michal Vasko71090fc2016-05-24 16:37:28 +02001623 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001624 } else if (!ti) {
1625 ERRARG("ti");
Michal Vasko71090fc2016-05-24 16:37:28 +02001626 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001627 } else if (!session) {
1628 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001629 return NC_MSG_ERROR;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001630 }
1631
Michal Vaskob05053d2016-01-22 16:12:06 +01001632 sock = nc_sock_connect(host, port);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001633 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001634 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01001635 }
1636
1637 *session = calloc(1, sizeof **session);
1638 if (!(*session)) {
1639 ERRMEM;
1640 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001641 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01001642 }
1643 (*session)->status = NC_STATUS_STARTING;
1644 (*session)->side = NC_SERVER;
1645 (*session)->ctx = server_opts.ctx;
1646 (*session)->flags = NC_SESSION_SHAREDCTX | NC_SESSION_CALLHOME;
Michal Vaskob05053d2016-01-22 16:12:06 +01001647 (*session)->host = lydict_insert(server_opts.ctx, host, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01001648 (*session)->port = port;
1649
1650 /* transport lock */
1651 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1652 if (!(*session)->ti_lock) {
1653 ERRMEM;
1654 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001655 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01001656 goto fail;
1657 }
1658 pthread_mutex_init((*session)->ti_lock, NULL);
1659
1660 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001661#ifdef NC_ENABLED_SSH
Michal Vaskob05053d2016-01-22 16:12:06 +01001662 if (ti == NC_TI_LIBSSH) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001663 /* OPTS LOCK */
1664 pthread_mutex_lock(&ssh_ch_opts_lock);
1665
Michal Vasko2cc4c682016-03-01 09:16:48 +01001666 (*session)->data = &ssh_ch_opts;
Michal Vasko0190bc32016-03-02 15:47:49 +01001667 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01001668 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001669
1670 /* OPTS UNLOCK */
1671 pthread_mutex_unlock(&ssh_ch_opts_lock);
1672
Michal Vasko71090fc2016-05-24 16:37:28 +02001673 if (ret < 0) {
1674 msgtype = NC_MSG_ERROR;
1675 goto fail;
1676 } else if (!ret) {
1677 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01001678 goto fail;
1679 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001680 } else
1681#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001682#ifdef NC_ENABLED_TLS
Michal Vasko3d865d22016-01-28 16:00:53 +01001683 if (ti == NC_TI_OPENSSL) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001684 /* OPTS LOCK */
1685 pthread_mutex_lock(&tls_ch_opts_lock);
1686
Michal Vasko2cc4c682016-03-01 09:16:48 +01001687 (*session)->data = &tls_ch_opts;
Michal Vasko0190bc32016-03-02 15:47:49 +01001688 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01001689 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001690
1691 /* OPTS UNLOCK */
1692 pthread_mutex_unlock(&tls_ch_opts_lock);
1693
Michal Vasko71090fc2016-05-24 16:37:28 +02001694 if (ret < 0) {
1695 msgtype = NC_MSG_ERROR;
1696 goto fail;
1697 } else if (!ret) {
1698 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01001699 goto fail;
1700 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001701 } else
1702#endif
1703 {
Michal Vaskob05053d2016-01-22 16:12:06 +01001704 ERRINT;
1705 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001706 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01001707 goto fail;
1708 }
1709
1710 /* assign new SID atomically */
1711 /* LOCK */
1712 pthread_spin_lock(&server_opts.sid_lock);
1713 (*session)->id = server_opts.new_session_id++;
1714 /* UNLOCK */
1715 pthread_spin_unlock(&server_opts.sid_lock);
1716
1717 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001718 msgtype = nc_handshake(*session);
1719 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01001720 goto fail;
1721 }
Michal Vaskof802fdc2016-07-28 15:47:00 +02001722 (*session)->session_start = (*session)->last_rpc = time(NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01001723 (*session)->status = NC_STATUS_RUNNING;
1724
Michal Vasko71090fc2016-05-24 16:37:28 +02001725 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01001726
1727fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001728 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01001729 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001730 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01001731}
1732
Radek Krejci53691be2016-02-22 13:58:37 +01001733#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02001734
Michal Vaskoc45ebd32016-05-25 11:17:36 +02001735API time_t
1736nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02001737{
1738 if (!session) {
1739 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02001740 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02001741 }
1742
Michal Vaskoc45ebd32016-05-25 11:17:36 +02001743 return session->session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02001744}