blob: 4772e82ee6dd29117b5e33fe446e5d3c6293490d [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
Michal Vasko3031aae2016-01-27 16:07:18 +010036extern struct nc_server_ssh_opts ssh_ch_opts;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010037extern pthread_mutex_t ssh_ch_opts_lock;
38
Michal Vasko3031aae2016-01-27 16:07:18 +010039extern struct nc_server_tls_opts tls_ch_opts;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010040extern pthread_mutex_t tls_ch_opts_lock;
Michal Vasko3031aae2016-01-27 16:07:18 +010041
42struct nc_endpt *
Michal Vaskoe2713da2016-08-22 16:06:40 +020043nc_server_endpt_lock(const char *name, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010044{
45 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010046 struct nc_endpt *endpt = NULL;
47
48 /* READ LOCK */
49 pthread_rwlock_rdlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010050
51 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +020052 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010053 endpt = &server_opts.endpts[i];
54 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010055 }
56 }
57
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010058 if (!endpt) {
59 ERR("Endpoint \"%s\" was not found.", name);
60 /* READ UNLOCK */
61 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
62 return NULL;
63 }
64
65 /* ENDPT LOCK */
66 pthread_mutex_lock(&endpt->endpt_lock);
67
Michal Vaskoe2713da2016-08-22 16:06:40 +020068 if (idx) {
69 *idx = i;
70 }
71
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010072 return endpt;
73}
74
75void
76nc_server_endpt_unlock(struct nc_endpt *endpt)
77{
78 /* ENDPT UNLOCK */
79 pthread_mutex_unlock(&endpt->endpt_lock);
80
81 /* READ UNLOCK */
Michal Vasko27562ad2016-02-02 15:50:39 +010082 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010083}
Michal Vasko086311b2016-01-08 09:53:11 +010084
Michal Vasko1a38c862016-01-15 15:50:07 +010085API void
86nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
87{
Michal Vasko45e53ae2016-04-07 11:46:03 +020088 if (!session) {
89 ERRARG("session");
90 return;
91 } else if (!reason) {
92 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +010093 return;
94 }
95
96 session->term_reason = reason;
97}
98
Michal Vasko086311b2016-01-08 09:53:11 +010099int
Michal Vaskof05562c2016-01-20 12:06:43 +0100100nc_sock_listen(const char *address, uint16_t port)
Michal Vasko086311b2016-01-08 09:53:11 +0100101{
102 const int optVal = 1;
103 const socklen_t optLen = sizeof(optVal);
104 int is_ipv4, sock;
105 struct sockaddr_storage saddr;
106
107 struct sockaddr_in *saddr4;
108 struct sockaddr_in6 *saddr6;
109
110
111 if (!strchr(address, ':')) {
112 is_ipv4 = 1;
113 } else {
114 is_ipv4 = 0;
115 }
116
117 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
118 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100119 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100120 goto fail;
121 }
122
123 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&optVal, optLen)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100124 ERR("Could not set socket SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100125 goto fail;
126 }
127
128 bzero(&saddr, sizeof(struct sockaddr_storage));
129 if (is_ipv4) {
130 saddr4 = (struct sockaddr_in *)&saddr;
131
132 saddr4->sin_family = AF_INET;
133 saddr4->sin_port = htons(port);
134
135 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100136 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100137 goto fail;
138 }
139
140 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100141 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100142 goto fail;
143 }
144
145 } else {
146 saddr6 = (struct sockaddr_in6 *)&saddr;
147
148 saddr6->sin6_family = AF_INET6;
149 saddr6->sin6_port = htons(port);
150
151 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100152 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100153 goto fail;
154 }
155
156 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100157 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100158 goto fail;
159 }
160 }
161
Michal Vaskofb89d772016-01-08 12:25:35 +0100162 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100163 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100164 goto fail;
165 }
166
167 return sock;
168
169fail:
170 if (sock > -1) {
171 close(sock);
172 }
173
174 return -1;
175}
176
177int
Michal Vasko3031aae2016-01-27 16:07:18 +0100178nc_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 +0100179{
180 uint16_t i;
181 struct pollfd *pfd;
182 struct sockaddr_storage saddr;
183 socklen_t saddr_len = sizeof(saddr);
Michal Vasko0190bc32016-03-02 15:47:49 +0100184 int ret, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100185
186 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100187 if (!pfd) {
188 ERRMEM;
189 return -1;
190 }
191
Michal Vasko086311b2016-01-08 09:53:11 +0100192 for (i = 0; i < bind_count; ++i) {
193 pfd[i].fd = binds[i].sock;
194 pfd[i].events = POLLIN;
195 pfd[i].revents = 0;
196 }
197
198 /* poll for a new connection */
199 errno = 0;
200 ret = poll(pfd, bind_count, timeout);
201 if (!ret) {
202 /* we timeouted */
203 free(pfd);
204 return 0;
205 } else if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100206 ERR("Poll failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100207 free(pfd);
208 return -1;
209 }
210
211 for (i = 0; i < bind_count; ++i) {
212 if (pfd[i].revents & POLLIN) {
213 sock = pfd[i].fd;
214 break;
215 }
216 }
217 free(pfd);
218
219 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100220 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100221 return -1;
222 }
223
224 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100225 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100226 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100227 return -1;
228 }
229
Michal Vasko0190bc32016-03-02 15:47:49 +0100230 /* make the socket non-blocking */
231 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
232 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100233 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100234 return -1;
235 }
236
Michal Vasko3031aae2016-01-27 16:07:18 +0100237 if (idx) {
238 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100239 }
240
Michal Vasko086311b2016-01-08 09:53:11 +0100241 /* host was requested */
242 if (host) {
243 if (saddr.ss_family == AF_INET) {
244 *host = malloc(15);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100245 if (*host) {
246 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, 15)) {
247 ERR("inet_ntop failed (%s).", strerror(errno));
248 free(*host);
249 *host = NULL;
250 }
Michal Vasko086311b2016-01-08 09:53:11 +0100251
Michal Vasko4eb3c312016-03-01 14:09:37 +0100252 if (port) {
253 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
254 }
255 } else {
256 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100257 }
258 } else if (saddr.ss_family == AF_INET6) {
259 *host = malloc(40);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100260 if (*host) {
261 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, 40)) {
262 ERR("inet_ntop failed (%s).", strerror(errno));
263 free(*host);
264 *host = NULL;
265 }
Michal Vasko086311b2016-01-08 09:53:11 +0100266
Michal Vasko4eb3c312016-03-01 14:09:37 +0100267 if (port) {
268 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
269 }
270 } else {
271 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100272 }
273 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100274 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100275 }
276 }
277
278 return ret;
279}
280
Michal Vasko05ba9df2016-01-13 14:40:27 +0100281static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100282nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100283{
284 const char *identifier = NULL, *version = NULL, *format = NULL;
285 char *model_data = NULL;
286 const struct lys_module *module;
287 struct nc_server_error *err;
288 struct lyd_node *child, *data = NULL;
Michal Vasko11d142a2016-01-19 15:58:24 +0100289 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100290
291 LY_TREE_FOR(rpc->child, child) {
292 if (!strcmp(child->schema->name, "identifier")) {
293 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
294 } else if (!strcmp(child->schema->name, "version")) {
295 version = ((struct lyd_node_leaf_list *)child)->value_str;
296 } else if (!strcmp(child->schema->name, "format")) {
297 format = ((struct lyd_node_leaf_list *)child)->value_str;
298 }
299 }
300
301 /* check version */
302 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100303 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
304 nc_err_set_msg(err, "The requested version is not supported.", "en");
305 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100306 }
307
308 /* check and get module with the name identifier */
309 module = ly_ctx_get_module(server_opts.ctx, identifier, version);
310 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200311 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
312 }
313 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100314 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
315 nc_err_set_msg(err, "The requested schema was not found.", "en");
316 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100317 }
318
319 /* check format */
320 if (!format || !strcmp(format, "yang")) {
321 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL);
322 } else if (!strcmp(format, "yin")) {
323 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL);
324 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100325 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
326 nc_err_set_msg(err, "The requested format is not supported.", "en");
327 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100328 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200329 if (!model_data) {
330 ERRINT;
331 return NULL;
332 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100333
Michal Vasko303245c2016-03-24 15:20:03 +0100334 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/output/data");
Michal Vaskod91f6e62016-04-05 11:34:22 +0200335 if (!sdata) {
336 ERRINT;
337 free(model_data);
338 return NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100339 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200340
Radek Krejci539efb62016-08-24 15:05:16 +0200341 data = lyd_new_path(NULL, server_opts.ctx, "/ietf-netconf-monitoring:get-schema/data", model_data,
342 LYD_ANYDATA_STRING, LYD_PATH_OPT_OUTPUT);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100343 if (!data) {
344 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200345 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100346 return NULL;
347 }
348
Radek Krejci36dfdb32016-09-01 16:56:35 +0200349 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100350}
351
352static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100353nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100354{
Michal Vasko428087d2016-01-14 16:04:28 +0100355 session->term_reason = NC_SESSION_TERM_CLOSED;
356 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100357}
358
Michal Vasko086311b2016-01-08 09:53:11 +0100359API int
360nc_server_init(struct ly_ctx *ctx)
361{
Michal Vasko05ba9df2016-01-13 14:40:27 +0100362 const struct lys_node *rpc;
363
Michal Vasko086311b2016-01-08 09:53:11 +0100364 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200365 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100366 return -1;
367 }
368
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100369 nc_init();
370
Michal Vasko05ba9df2016-01-13 14:40:27 +0100371 /* set default <get-schema> callback if not specified */
Michal Vasko303245c2016-03-24 15:20:03 +0100372 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema");
Michal Vaskofd100c92016-03-01 15:23:46 +0100373 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100374 lys_set_private(rpc, nc_clb_default_get_schema);
375 }
376
377 /* set default <close-session> callback if not specififed */
Michal Vasko303245c2016-03-24 15:20:03 +0100378 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session");
Michal Vaskofd100c92016-03-01 15:23:46 +0100379 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100380 lys_set_private(rpc, nc_clb_default_close_session);
381 }
382
Michal Vasko086311b2016-01-08 09:53:11 +0100383 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100384
385 server_opts.new_session_id = 1;
386 pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
387
Michal Vasko086311b2016-01-08 09:53:11 +0100388 return 0;
389}
390
Michal Vaskob48aa812016-01-18 14:13:09 +0100391API void
392nc_server_destroy(void)
393{
394 pthread_spin_destroy(&server_opts.sid_lock);
395
Radek Krejci53691be2016-02-22 13:58:37 +0100396#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200397 nc_server_del_endpt(NULL);
Michal Vaskob48aa812016-01-18 14:13:09 +0100398#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100399 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100400}
401
Michal Vasko086311b2016-01-08 09:53:11 +0100402API int
403nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
404{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200405 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
406 ERRARG("basic_mode");
407 return -1;
408 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
409 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100410 return -1;
411 }
412
413 server_opts.wd_basic_mode = basic_mode;
414 server_opts.wd_also_supported = also_supported;
415 return 0;
416}
417
Michal Vasko1a38c862016-01-15 15:50:07 +0100418API void
Michal Vasko55f03972016-04-13 08:56:01 +0200419nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
420{
421 if (!basic_mode && !also_supported) {
422 ERRARG("basic_mode and also_supported");
423 return;
424 }
425
426 if (basic_mode) {
427 *basic_mode = server_opts.wd_basic_mode;
428 }
429 if (also_supported) {
430 *also_supported = server_opts.wd_also_supported;
431 }
432}
433
434API void
Michal Vasko086311b2016-01-08 09:53:11 +0100435nc_server_set_capab_interleave(int interleave_support)
436{
437 if (interleave_support) {
438 server_opts.interleave_capab = 1;
439 } else {
440 server_opts.interleave_capab = 0;
441 }
Michal Vasko086311b2016-01-08 09:53:11 +0100442}
443
Michal Vasko55f03972016-04-13 08:56:01 +0200444API int
445nc_server_get_capab_interleave(void)
446{
447 return server_opts.interleave_capab;
448}
449
Michal Vasko1a38c862016-01-15 15:50:07 +0100450API void
Michal Vasko086311b2016-01-08 09:53:11 +0100451nc_server_set_hello_timeout(uint16_t hello_timeout)
452{
Michal Vasko086311b2016-01-08 09:53:11 +0100453 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100454}
455
Michal Vasko55f03972016-04-13 08:56:01 +0200456API uint16_t
457nc_server_get_hello_timeout(void)
458{
459 return server_opts.hello_timeout;
460}
461
Michal Vasko1a38c862016-01-15 15:50:07 +0100462API void
Michal Vasko086311b2016-01-08 09:53:11 +0100463nc_server_set_idle_timeout(uint16_t idle_timeout)
464{
Michal Vasko086311b2016-01-08 09:53:11 +0100465 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100466}
467
Michal Vasko55f03972016-04-13 08:56:01 +0200468API uint16_t
469nc_server_get_idle_timeout(void)
470{
471 return server_opts.idle_timeout;
472}
473
Michal Vasko71090fc2016-05-24 16:37:28 +0200474API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100475nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100476{
Michal Vasko71090fc2016-05-24 16:37:28 +0200477 NC_MSG_TYPE msgtype;
478
Michal Vasko45e53ae2016-04-07 11:46:03 +0200479 if (!server_opts.ctx) {
480 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200481 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200482 } else if (fdin < 0) {
483 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200484 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200485 } else if (fdout < 0) {
486 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200487 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200488 } else if (!username) {
489 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200490 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200491 } else if (!session) {
492 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200493 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100494 }
495
496 /* prepare session structure */
Michal Vasko1a38c862016-01-15 15:50:07 +0100497 *session = calloc(1, sizeof **session);
498 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100499 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200500 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100501 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100502 (*session)->status = NC_STATUS_STARTING;
503 (*session)->side = NC_SERVER;
Michal Vasko086311b2016-01-08 09:53:11 +0100504
505 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100506 (*session)->ti_type = NC_TI_FD;
507 (*session)->ti.fd.in = fdin;
508 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100509
510 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100511 (*session)->flags = NC_SESSION_SHAREDCTX;
512 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100513
Michal Vaskob48aa812016-01-18 14:13:09 +0100514 /* assign new SID atomically */
515 pthread_spin_lock(&server_opts.sid_lock);
516 (*session)->id = server_opts.new_session_id++;
517 pthread_spin_unlock(&server_opts.sid_lock);
518
Michal Vasko086311b2016-01-08 09:53:11 +0100519 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200520 msgtype = nc_handshake(*session);
521 if (msgtype != NC_MSG_HELLO) {
522 nc_session_free(*session, NULL);
523 *session = NULL;
524 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100525 }
Michal Vaskof8352352016-05-24 09:11:36 +0200526 (*session)->session_start = (*session)->last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +0100527 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100528
Michal Vasko71090fc2016-05-24 16:37:28 +0200529 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100530}
Michal Vasko9e036d52016-01-08 10:49:26 +0100531
Michal Vaskobdcf2362016-07-26 11:35:43 +0200532static void
533nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
534{
535 uint8_t i, found = 0;
536
537 for (i = 0; i < ps->queue_len; ++i) {
538 /* idx round buffer adjust */
539 if (ps->queue_begin + i == NC_PS_QUEUE_SIZE) {
540 i = -ps->queue_begin;
541 }
542
543 if (found) {
544 /* move the value back one place */
545 if (ps->queue[ps->queue_begin + i] == id) {
546 /* another equal value, simply cannot be */
547 ERRINT;
548 }
549
550 if (ps->queue_begin + i == 0) {
551 ps->queue[NC_PS_QUEUE_SIZE - 1] = ps->queue[ps->queue_begin + i];
552 } else {
553 ps->queue[ps->queue_begin + i - 1] = ps->queue[ps->queue_begin + i];
554 }
555 } else if (ps->queue[ps->queue_begin + i] == id) {
556 /* found our id, there can be no more equal valid values */
557 found = 1;
558 }
559 }
560
561 if (!found) {
562 ERRINT;
563 }
564 --ps->queue_len;
565}
566
Michal Vaskof04a52a2016-04-07 10:52:10 +0200567int
Michal Vasko227f8ff2016-07-26 14:08:59 +0200568nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200569{
570 int ret;
Michal Vaskobdcf2362016-07-26 11:35:43 +0200571 uint8_t queue_last;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200572 struct timespec ts;
573
Radek Krejci7ac16052016-07-15 11:48:18 +0200574 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200575 ts.tv_sec += NC_READ_TIMEOUT;
576
577 /* LOCK */
578 ret = pthread_mutex_timedlock(&ps->lock, &ts);
579 if (ret) {
Michal Vasko227f8ff2016-07-26 14:08:59 +0200580 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200581 return -1;
582 }
583
584 /* get a unique queue value (by adding 1 to the last added value, if any) */
585 if (ps->queue_len) {
586 queue_last = ps->queue_begin + ps->queue_len - 1;
587 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
588 queue_last -= NC_PS_QUEUE_SIZE;
589 }
Michal Vaskobdcf2362016-07-26 11:35:43 +0200590 *id = ps->queue[queue_last] + 1;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200591 } else {
Michal Vaskobdcf2362016-07-26 11:35:43 +0200592 *id = 0;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200593 }
594
595 /* add ourselves into the queue */
596 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko227f8ff2016-07-26 14:08:59 +0200597 ERR("%s: pollsession queue too small.", func);
Michal Vasko8c242532016-07-26 12:23:24 +0200598 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200599 return -1;
600 }
601 ++ps->queue_len;
602 queue_last = ps->queue_begin + ps->queue_len - 1;
603 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
604 queue_last -= NC_PS_QUEUE_SIZE;
605 }
Michal Vaskobdcf2362016-07-26 11:35:43 +0200606 ps->queue[queue_last] = *id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200607
608 /* is it our turn? */
Michal Vaskobdcf2362016-07-26 11:35:43 +0200609 while (ps->queue[ps->queue_begin] != *id) {
Radek Krejci7ac16052016-07-15 11:48:18 +0200610 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200611 ts.tv_sec += NC_READ_TIMEOUT;
612
613 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
614 if (ret) {
Michal Vasko227f8ff2016-07-26 14:08:59 +0200615 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200616 /* remove ourselves from the queue */
Michal Vaskobdcf2362016-07-26 11:35:43 +0200617 nc_ps_queue_remove_id(ps, *id);
618 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200619 return -1;
620 }
621 }
622
Michal Vaskobe86fe32016-04-07 10:43:03 +0200623 /* UNLOCK */
624 pthread_mutex_unlock(&ps->lock);
625
626 return 0;
627}
628
Michal Vaskof04a52a2016-04-07 10:52:10 +0200629int
Michal Vasko227f8ff2016-07-26 14:08:59 +0200630nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200631{
632 int ret;
633 struct timespec ts;
634
Radek Krejci7ac16052016-07-15 11:48:18 +0200635 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200636 ts.tv_sec += NC_READ_TIMEOUT;
637
638 /* LOCK */
639 ret = pthread_mutex_timedlock(&ps->lock, &ts);
640 if (ret) {
Michal Vasko227f8ff2016-07-26 14:08:59 +0200641 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200642 ret = -1;
643 }
644
Michal Vaskobdcf2362016-07-26 11:35:43 +0200645 /* we must be the first, it was our turn after all, right? */
646 if (ps->queue[ps->queue_begin] != id) {
647 ERRINT;
648 return -1;
649 }
650
Michal Vaskobe86fe32016-04-07 10:43:03 +0200651 /* remove ourselves from the queue */
Michal Vaskobdcf2362016-07-26 11:35:43 +0200652 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200653
654 /* broadcast to all other threads that the queue moved */
655 pthread_cond_broadcast(&ps->cond);
656
Michal Vaskobe86fe32016-04-07 10:43:03 +0200657 /* UNLOCK */
658 if (!ret) {
659 pthread_mutex_unlock(&ps->lock);
660 }
661
662 return ret;
663}
664
Michal Vasko428087d2016-01-14 16:04:28 +0100665API struct nc_pollsession *
666nc_ps_new(void)
667{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100668 struct nc_pollsession *ps;
669
670 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100671 if (!ps) {
672 ERRMEM;
673 return NULL;
674 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200675 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100676 pthread_mutex_init(&ps->lock, NULL);
677
678 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100679}
680
681API void
682nc_ps_free(struct nc_pollsession *ps)
683{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100684 if (!ps) {
685 return;
686 }
687
Michal Vaskobe86fe32016-04-07 10:43:03 +0200688 if (ps->queue_len) {
689 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
690 }
691
Michal Vasko3a715132016-01-21 15:40:31 +0100692 free(ps->pfds);
Michal Vasko428087d2016-01-14 16:04:28 +0100693 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100694 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200695 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100696
Michal Vasko428087d2016-01-14 16:04:28 +0100697 free(ps);
698}
699
700API int
701nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
702{
Michal Vaskobdcf2362016-07-26 11:35:43 +0200703 uint8_t q_id;
704
Michal Vasko45e53ae2016-04-07 11:46:03 +0200705 if (!ps) {
706 ERRARG("ps");
707 return -1;
708 } else if (!session) {
709 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +0100710 return -1;
711 }
712
Michal Vasko48a63ed2016-03-01 09:48:21 +0100713 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +0200714 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200715 return -1;
716 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100717
Michal Vasko428087d2016-01-14 16:04:28 +0100718 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100719 ps->pfds = nc_realloc(ps->pfds, ps->session_count * sizeof *ps->pfds);
720 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
721 if (!ps->pfds || !ps->sessions) {
722 ERRMEM;
723 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +0200724 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100725 return -1;
726 }
Michal Vasko428087d2016-01-14 16:04:28 +0100727
728 switch (session->ti_type) {
729 case NC_TI_FD:
Michal Vasko3a715132016-01-21 15:40:31 +0100730 ps->pfds[ps->session_count - 1].fd = session->ti.fd.in;
Michal Vasko428087d2016-01-14 16:04:28 +0100731 break;
732
Radek Krejci53691be2016-02-22 13:58:37 +0100733#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100734 case NC_TI_LIBSSH:
Michal Vasko3a715132016-01-21 15:40:31 +0100735 ps->pfds[ps->session_count - 1].fd = ssh_get_fd(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100736 break;
737#endif
738
Radek Krejci53691be2016-02-22 13:58:37 +0100739#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100740 case NC_TI_OPENSSL:
Michal Vasko3a715132016-01-21 15:40:31 +0100741 ps->pfds[ps->session_count - 1].fd = SSL_get_rfd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100742 break;
743#endif
744
745 default:
746 ERRINT;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100747 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +0200748 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100749 return -1;
750 }
Michal Vasko3a715132016-01-21 15:40:31 +0100751 ps->pfds[ps->session_count - 1].events = POLLIN;
752 ps->pfds[ps->session_count - 1].revents = 0;
753 ps->sessions[ps->session_count - 1] = session;
Michal Vasko428087d2016-01-14 16:04:28 +0100754
Michal Vasko48a63ed2016-03-01 09:48:21 +0100755 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +0200756 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100757}
758
Michal Vasko48a63ed2016-03-01 09:48:21 +0100759static int
Radek Krejcid5f978f2016-03-03 13:14:45 +0100760_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +0100761{
762 uint16_t i;
763
Radek Krejcid5f978f2016-03-03 13:14:45 +0100764 if (index >= 0) {
765 i = (uint16_t)index;
766 goto remove;
767 }
Michal Vasko428087d2016-01-14 16:04:28 +0100768 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100769 if (ps->sessions[i] == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +0100770remove:
Michal Vasko428087d2016-01-14 16:04:28 +0100771 --ps->session_count;
Michal Vasko58005732016-02-02 15:50:52 +0100772 if (i < ps->session_count) {
773 ps->sessions[i] = ps->sessions[ps->session_count];
774 memcpy(&ps->pfds[i], &ps->pfds[ps->session_count], sizeof *ps->pfds);
775 } else if (!ps->session_count) {
776 free(ps->sessions);
777 ps->sessions = NULL;
778 free(ps->pfds);
779 ps->pfds = NULL;
780 }
Michal Vasko428087d2016-01-14 16:04:28 +0100781 return 0;
782 }
783 }
784
Michal Vaskof0537d82016-01-29 14:42:38 +0100785 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +0100786}
787
Michal Vasko48a63ed2016-03-01 09:48:21 +0100788API int
789nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
790{
Michal Vaskobdcf2362016-07-26 11:35:43 +0200791 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200792 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100793
Michal Vasko45e53ae2016-04-07 11:46:03 +0200794 if (!ps) {
795 ERRARG("ps");
796 return -1;
797 } else if (!session) {
798 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +0100799 return -1;
800 }
801
802 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +0200803 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200804 return -1;
805 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100806
Radek Krejcid5f978f2016-03-03 13:14:45 +0100807 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100808
809 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +0200810 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100811
Michal Vaskobe86fe32016-04-07 10:43:03 +0200812 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100813}
814
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100815API uint16_t
816nc_ps_session_count(struct nc_pollsession *ps)
817{
Michal Vaskobdcf2362016-07-26 11:35:43 +0200818 uint8_t q_id;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100819 uint16_t count;
820
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100821 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200822 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100823 return 0;
824 }
825
Michal Vasko48a63ed2016-03-01 09:48:21 +0100826 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +0200827 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200828 return -1;
829 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100830
831 count = ps->session_count;
832
833 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +0200834 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100835
836 return count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100837}
838
Michal Vasko71090fc2016-05-24 16:37:28 +0200839/* must be called holding the session lock!
840 * returns: NC_PSPOLL_ERROR,
841 * NC_PSPOLL_BAD_RPC,
842 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
843 * NC_PSPOLL_RPC
844 */
845static int
Michal Vasko428087d2016-01-14 16:04:28 +0100846nc_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
847{
848 struct lyxml_elem *xml = NULL;
849 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200850 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200851 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100852
Michal Vasko45e53ae2016-04-07 11:46:03 +0200853 if (!session) {
854 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200855 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200856 } else if (!rpc) {
857 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +0200858 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100859 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100860 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200861 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100862 }
863
864 msgtype = nc_read_msg(session, &xml);
865
866 switch (msgtype) {
867 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +0200868 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100869 if (!*rpc) {
870 ERRMEM;
871 goto error;
872 }
Michal Vaskoca4a2422016-02-02 12:17:14 +0100873
Radek Krejcif93c7d42016-04-06 13:41:15 +0200874 ly_errno = LY_SUCCESS;
Michal Vasko68b3f292016-09-16 12:00:32 +0200875 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child, LYD_OPT_RPC | LYD_OPT_DESTRUCT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +0100876 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +0200877 /* parsing RPC failed */
Radek Krejci877e1822016-04-06 16:37:43 +0200878 reply = nc_server_reply_err(nc_err_libyang());
Radek Krejci844662e2016-04-13 16:54:43 +0200879 ret = nc_write_msg(session, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200880 nc_server_reply_free(reply);
881 if (ret == -1) {
882 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200883 }
Michal Vasko71090fc2016-05-24 16:37:28 +0200884 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
885 } else {
886 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +0100887 }
Michal Vasko428087d2016-01-14 16:04:28 +0100888 (*rpc)->root = xml;
889 break;
890 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +0100891 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200892 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100893 goto error;
894 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +0100895 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200896 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100897 goto error;
898 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +0100899 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200900 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100901 goto error;
902 default:
Michal Vasko71090fc2016-05-24 16:37:28 +0200903 /* NC_MSG_ERROR,
Michal Vasko428087d2016-01-14 16:04:28 +0100904 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
905 */
Michal Vasko71090fc2016-05-24 16:37:28 +0200906 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100907 break;
908 }
909
Michal Vasko71090fc2016-05-24 16:37:28 +0200910 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100911
912error:
913 /* cleanup */
914 lyxml_free(server_opts.ctx, xml);
915
Michal Vasko71090fc2016-05-24 16:37:28 +0200916 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100917}
918
Michal Vasko71090fc2016-05-24 16:37:28 +0200919/* must be called holding the session lock!
920 * returns: NC_PSPOLL_ERROR,
921 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
922 * NC_PSPOLL_REPLY_ERROR,
923 * 0
924 */
925static int
Michal Vasko428087d2016-01-14 16:04:28 +0100926nc_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
927{
928 nc_rpc_clb clb;
929 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +0200930 struct lys_node *rpc_act = NULL;
931 struct lyd_node *next, *elem;
Michal Vasko71090fc2016-05-24 16:37:28 +0200932 int ret = 0, r;
Michal Vasko428087d2016-01-14 16:04:28 +0100933
Michal Vasko4a827e52016-03-03 10:59:00 +0100934 if (!rpc) {
935 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200936 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +0100937 }
938
Michal Vasko90e8e692016-07-13 12:27:57 +0200939 if (rpc->tree->schema->nodetype == LYS_RPC) {
940 /* RPC */
941 rpc_act = rpc->tree->schema;
942 } else {
943 /* action */
944 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
945 if (elem->schema->nodetype == LYS_ACTION) {
946 rpc_act = elem->schema;
947 break;
948 }
949 LY_TREE_DFS_END(rpc->tree, next, elem);
950 }
951 if (!rpc_act) {
952 ERRINT;
953 return NC_PSPOLL_ERROR;
954 }
955 }
956
957 if (!rpc_act->priv) {
958 /* no callback, reply with a not-implemented error */
Michal Vasko1a38c862016-01-15 15:50:07 +0100959 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +0100960 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +0200961 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +0100962 reply = clb(rpc->tree, session);
963 }
964
965 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100966 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +0100967 }
Michal Vasko71090fc2016-05-24 16:37:28 +0200968 r = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
969 if (reply->type == NC_RPL_ERROR) {
970 ret |= NC_PSPOLL_REPLY_ERROR;
971 }
972 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +0100973
Michal Vasko71090fc2016-05-24 16:37:28 +0200974 if (r == -1) {
975 ERR("Session %u: failed to write reply.", session->id);
976 ret |= NC_PSPOLL_ERROR;
977 }
Michal Vasko428087d2016-01-14 16:04:28 +0100978
979 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
980 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
981 session->status = NC_STATUS_INVALID;
982 }
983
Michal Vasko71090fc2016-05-24 16:37:28 +0200984 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100985}
986
987API int
Michal Vasko71090fc2016-05-24 16:37:28 +0200988nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
Michal Vasko428087d2016-01-14 16:04:28 +0100989{
990 int ret;
Michal Vaskobdcf2362016-07-26 11:35:43 +0200991 uint8_t q_id;
Michal Vasko3512e402016-01-28 16:22:34 +0100992 uint16_t i;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100993 time_t cur_time;
Michal Vasko71090fc2016-05-24 16:37:28 +0200994 struct nc_session *cur_session;
Michal Vasko4a827e52016-03-03 10:59:00 +0100995 struct nc_server_rpc *rpc = NULL;
Michal Vasko428087d2016-01-14 16:04:28 +0100996
997 if (!ps || !ps->session_count) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200998 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +0200999 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001000 }
1001
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001002 cur_time = time(NULL);
1003
Michal Vasko48a63ed2016-03-01 09:48:21 +01001004 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001005 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001006 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001007 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001008
Michal Vasko428087d2016-01-14 16:04:28 +01001009 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +01001010 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1011 ERR("Session %u: session not running.", ps->sessions[i]->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001012 ret = NC_PSPOLL_ERROR;
1013 if (session) {
1014 *session = ps->sessions[i];
1015 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001016 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001017 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001018
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001019 /* TODO invalidate only sessions without subscription */
Michal Vasko15dfe722016-07-28 15:47:25 +02001020 if (server_opts.idle_timeout && (cur_time >= ps->sessions[i]->last_rpc + server_opts.idle_timeout)) {
Michal Vasko3a715132016-01-21 15:40:31 +01001021 ERR("Session %u: session idle timeout elapsed.", ps->sessions[i]->id);
1022 ps->sessions[i]->status = NC_STATUS_INVALID;
1023 ps->sessions[i]->term_reason = NC_SESSION_TERM_TIMEOUT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001024 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1025 if (session) {
1026 *session = ps->sessions[i];
1027 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001028 goto finish;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001029 }
1030
Michal Vasko3a715132016-01-21 15:40:31 +01001031 if (ps->pfds[i].revents) {
Michal Vaskobd8ef262016-01-20 11:09:27 +01001032 break;
1033 }
Michal Vasko428087d2016-01-14 16:04:28 +01001034 }
1035
Michal Vaskobd8ef262016-01-20 11:09:27 +01001036 if (i == ps->session_count) {
Radek Krejci53691be2016-02-22 13:58:37 +01001037#ifdef NC_ENABLED_SSH
Michal Vasko3a715132016-01-21 15:40:31 +01001038retry_poll:
Michal Vasko3512e402016-01-28 16:22:34 +01001039#endif
Michal Vaskobd8ef262016-01-20 11:09:27 +01001040 /* no leftover event */
1041 i = 0;
Michal Vasko3a715132016-01-21 15:40:31 +01001042 ret = poll(ps->pfds, ps->session_count, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001043 if (ret < 0) {
1044 ERR("Poll failed (%s).", strerror(errno));
1045 ret = NC_PSPOLL_ERROR;
1046 goto finish;
1047 } else if (!ret) {
1048 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001049 goto finish;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001050 }
Michal Vasko428087d2016-01-14 16:04:28 +01001051 }
1052
Michal Vaskobd8ef262016-01-20 11:09:27 +01001053 /* find the first fd with POLLIN, we don't care if there are more now */
1054 for (; i < ps->session_count; ++i) {
Michal Vasko46eac552016-05-30 15:27:25 +02001055 if (ps->pfds[i].revents & (POLLHUP | POLLNVAL)) {
Michal Vasko3a715132016-01-21 15:40:31 +01001056 ERR("Session %u: communication socket unexpectedly closed.", ps->sessions[i]->id);
1057 ps->sessions[i]->status = NC_STATUS_INVALID;
1058 ps->sessions[i]->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko71090fc2016-05-24 16:37:28 +02001059 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1060 if (session) {
1061 *session = ps->sessions[i];
1062 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001063 goto finish;
Michal Vasko3a715132016-01-21 15:40:31 +01001064 } else if (ps->pfds[i].revents & POLLERR) {
1065 ERR("Session %u: communication socket error.", ps->sessions[i]->id);
1066 ps->sessions[i]->status = NC_STATUS_INVALID;
1067 ps->sessions[i]->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko71090fc2016-05-24 16:37:28 +02001068 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1069 if (session) {
1070 *session = ps->sessions[i];
1071 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001072 goto finish;
Michal Vasko3a715132016-01-21 15:40:31 +01001073 } else if (ps->pfds[i].revents & POLLIN) {
Radek Krejci53691be2016-02-22 13:58:37 +01001074#ifdef NC_ENABLED_SSH
Michal Vasko96164bf2016-01-21 15:41:58 +01001075 if (ps->sessions[i]->ti_type == NC_TI_LIBSSH) {
Michal Vasko3512e402016-01-28 16:22:34 +01001076 uint16_t j;
1077
Michal Vasko96164bf2016-01-21 15:41:58 +01001078 /* things are not that simple with SSH... */
Michal Vasko62be1ce2016-03-03 13:24:52 +01001079 ret = nc_ssh_pollin(ps->sessions[i], timeout);
Michal Vasko96164bf2016-01-21 15:41:58 +01001080
1081 /* clear POLLIN on sessions sharing this session's SSH session */
Michal Vasko71090fc2016-05-24 16:37:28 +02001082 if (ret & (NC_PSPOLL_RPC | NC_PSPOLL_SSH_MSG | NC_PSPOLL_SSH_CHANNEL)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001083 for (j = i + 1; j < ps->session_count; ++j) {
1084 if (ps->pfds[j].fd == ps->pfds[i].fd) {
1085 ps->pfds[j].revents = 0;
1086 }
1087 }
1088 }
1089
Michal Vasko71090fc2016-05-24 16:37:28 +02001090 /* SSH message only */
1091 if (!(ret & (NC_PSPOLL_RPC | NC_PSPOLL_PENDING))) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001092 ps->pfds[i].revents = 0;
Michal Vasko71090fc2016-05-24 16:37:28 +02001093 if (session) {
1094 *session = ps->sessions[i];
1095 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001096 goto finish;
Michal Vasko96164bf2016-01-21 15:41:58 +01001097
1098 /* event occurred on some other channel */
Michal Vasko71090fc2016-05-24 16:37:28 +02001099 } else if (ret & NC_PSPOLL_PENDING) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001100 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001101 if (i == ps->session_count - 1) {
1102 /* last session and it is not the right channel, ... */
Michal Vasko8c748832016-02-03 15:32:16 +01001103 if (!timeout) {
Michal Vasko428087d2016-01-14 16:04:28 +01001104 /* ... timeout is 0, so that is it */
Michal Vasko71090fc2016-05-24 16:37:28 +02001105 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001106 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001107 }
Michal Vasko8c748832016-02-03 15:32:16 +01001108 /* ... retry polling reasonable time apart ... */
1109 usleep(NC_TIMEOUT_STEP);
1110 if (timeout > 0) {
1111 /* ... and decrease timeout, if not -1 */
Michal Vasko7b38e232016-02-26 15:01:07 +01001112 timeout -= NC_TIMEOUT_STEP * 1000;
Michal Vasko8c748832016-02-03 15:32:16 +01001113 }
1114 goto retry_poll;
Michal Vasko428087d2016-01-14 16:04:28 +01001115 }
1116 /* check other sessions */
1117 continue;
Michal Vasko428087d2016-01-14 16:04:28 +01001118 }
1119 }
Radek Krejci53691be2016-02-22 13:58:37 +01001120#endif /* NC_ENABLED_SSH */
Michal Vasko428087d2016-01-14 16:04:28 +01001121
Michal Vaskobd8ef262016-01-20 11:09:27 +01001122 /* we are going to process it now */
Michal Vasko3a715132016-01-21 15:40:31 +01001123 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001124 break;
1125 }
1126 }
1127
1128 if (i == ps->session_count) {
1129 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001130 ret = NC_PSPOLL_ERROR;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001131 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001132 }
1133
1134 /* this is the session with some data available for reading */
Michal Vasko71090fc2016-05-24 16:37:28 +02001135 cur_session = ps->sessions[i];
1136 if (session) {
1137 *session = cur_session;
1138 }
Michal Vasko428087d2016-01-14 16:04:28 +01001139
Michal Vaskobd8ef262016-01-20 11:09:27 +01001140 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal Vasko71090fc2016-05-24 16:37:28 +02001141 ret = nc_timedlock(cur_session->ti_lock, timeout);
1142 if (ret < 0) {
1143 ret = NC_PSPOLL_ERROR;
1144 goto finish;
1145 } else if (!ret) {
1146 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001147 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001148 }
1149
Michal Vasko71090fc2016-05-24 16:37:28 +02001150 ret = nc_recv_rpc(cur_session, &rpc);
1151 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1152 pthread_mutex_unlock(cur_session->ti_lock);
1153 if (cur_session->status != NC_STATUS_RUNNING) {
1154 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001155 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001156 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001157 }
1158
Michal Vasko71090fc2016-05-24 16:37:28 +02001159 cur_session->last_rpc = time(NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001160
Michal Vasko428087d2016-01-14 16:04:28 +01001161 /* process RPC */
Michal Vasko71090fc2016-05-24 16:37:28 +02001162 ret |= nc_send_reply(cur_session, rpc);
Michal Vasko428087d2016-01-14 16:04:28 +01001163
Michal Vasko71090fc2016-05-24 16:37:28 +02001164 pthread_mutex_unlock(cur_session->ti_lock);
1165 if (cur_session->status != NC_STATUS_RUNNING) {
1166 ret |= NC_PSPOLL_SESSION_TERM;
1167 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1168 ret |= NC_PSPOLL_SESSION_ERROR;
1169 }
Michal Vasko428087d2016-01-14 16:04:28 +01001170 }
Radek Krejcif93c7d42016-04-06 13:41:15 +02001171
Michal Vaskoca4a2422016-02-02 12:17:14 +01001172 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vaskobd8ef262016-01-20 11:09:27 +01001173
1174 /* is there some other socket waiting? */
1175 for (++i; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +01001176 if (ps->pfds[i].revents) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001177 ret |= NC_PSPOLL_PENDING;
1178 break;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001179 }
1180 }
1181
Michal Vasko48a63ed2016-03-01 09:48:21 +01001182finish:
1183 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001184 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001185 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001186}
1187
Michal Vaskod09eae62016-02-01 10:32:52 +01001188API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001189nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001190{
Michal Vaskobdcf2362016-07-26 11:35:43 +02001191 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001192 uint16_t i;
1193 struct nc_session *session;
1194
Michal Vasko9a25e932016-02-01 10:36:42 +01001195 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001196 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001197 return;
1198 }
1199
Michal Vasko48a63ed2016-03-01 09:48:21 +01001200 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001201 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001202 return;
1203 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001204
Michal Vasko48a63ed2016-03-01 09:48:21 +01001205 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001206 for (i = 0; i < ps->session_count; i++) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001207 nc_session_free(ps->sessions[i], data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001208 }
1209 free(ps->sessions);
1210 ps->sessions = NULL;
1211 free(ps->pfds);
1212 ps->pfds = NULL;
1213 ps->session_count = 0;
1214 } else {
1215 for (i = 0; i < ps->session_count; ) {
1216 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1217 session = ps->sessions[i];
Radek Krejcid5f978f2016-03-03 13:14:45 +01001218 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001219 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001220 continue;
1221 }
1222
1223 ++i;
1224 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001225 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001226
1227 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001228 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001229}
1230
Radek Krejci53691be2016-02-22 13:58:37 +01001231#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001232
Michal Vaskoe2713da2016-08-22 16:06:40 +02001233API int
1234nc_server_add_endpt(const char *name)
Michal Vasko9e036d52016-01-08 10:49:26 +01001235{
Michal Vasko3031aae2016-01-27 16:07:18 +01001236 uint16_t i;
Radek Krejci53691be2016-02-22 13:58:37 +01001237#ifdef NC_ENABLED_SSH
Michal Vaskoe2713da2016-08-22 16:06:40 +02001238 uint16_t bind_ssh_idx;
1239#endif
1240#ifdef NC_ENABLED_TLS
1241 uint16_t bind_tls_idx;
Michal Vasko08a629a2016-02-02 12:20:47 +01001242#endif
Michal Vasko9e036d52016-01-08 10:49:26 +01001243
Michal Vasko45e53ae2016-04-07 11:46:03 +02001244 if (!name) {
1245 ERRARG("name");
1246 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001247 }
1248
Michal Vasko51e514d2016-02-02 15:51:52 +01001249 /* WRITE LOCK */
1250 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001251
1252 /* check name uniqueness */
1253 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001254 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001255 ERR("Endpoint \"%s\" already exists.", name);
Michal Vasko51e514d2016-02-02 15:51:52 +01001256 /* WRITE UNLOCK */
Michal Vasko9faf1c82016-02-01 13:26:19 +01001257 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001258 return -1;
1259 }
1260 }
1261
Michal Vasko3031aae2016-01-27 16:07:18 +01001262 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001263 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001264 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001265 ERRMEM;
1266 /* WRITE UNLOCK */
1267 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001268 return -1;
1269 }
1270 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
1271
1272#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1273 server_opts.binds = nc_realloc(server_opts.binds, 2 * server_opts.endpt_count * sizeof *server_opts.binds);
1274 bind_ssh_idx = (server_opts.endpt_count - 1) * 2;
1275 bind_tls_idx = (server_opts.endpt_count - 1) * 2 + 1;
1276#else
1277 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
1278# ifdef NC_ENABLED_SSH
1279 bind_ssh_idx = server_opts.endpt_count - 1;
1280# endif
1281# ifdef NC_ENABLED_TLS
1282 bind_tls_idx = server_opts.endpt_count - 1;
1283# endif
1284#endif
1285 if (!server_opts.binds) {
1286 ERRMEM;
1287 /* WRITE UNLOCK */
1288 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001289 return -1;
1290 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001291
Radek Krejci53691be2016-02-22 13:58:37 +01001292#ifdef NC_ENABLED_SSH
Michal Vaskoe2713da2016-08-22 16:06:40 +02001293 server_opts.binds[bind_ssh_idx].address = NULL;
1294 server_opts.binds[bind_ssh_idx].port = 0;
1295 server_opts.binds[bind_ssh_idx].sock = -1;
1296 server_opts.binds[bind_ssh_idx].ti = NC_TI_LIBSSH;
Michal Vasko08a629a2016-02-02 12:20:47 +01001297
Michal Vaskoe2713da2016-08-22 16:06:40 +02001298 server_opts.endpts[server_opts.endpt_count - 1].ssh_opts = calloc(1, sizeof(struct nc_server_ssh_opts));
1299 if (!server_opts.endpts[server_opts.endpt_count - 1].ssh_opts) {
1300 ERRMEM;
1301 /* WRITE UNLOCK */
1302 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1303 return -1;
Michal Vasko3031aae2016-01-27 16:07:18 +01001304 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001305 /* set default values */
1306 server_opts.endpts[server_opts.endpt_count - 1].ssh_opts->auth_methods =
1307 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1308 server_opts.endpts[server_opts.endpt_count - 1].ssh_opts->auth_attempts = 3;
1309 server_opts.endpts[server_opts.endpt_count - 1].ssh_opts->auth_timeout = 10;
1310#endif
1311
1312#ifdef NC_ENABLED_TLS
1313 server_opts.binds[bind_tls_idx].address = NULL;
1314 server_opts.binds[bind_tls_idx].port = 0;
1315 server_opts.binds[bind_tls_idx].sock = -1;
1316 server_opts.binds[bind_tls_idx].ti = NC_TI_OPENSSL;
1317
1318 server_opts.endpts[server_opts.endpt_count - 1].tls_opts = calloc(1, sizeof(struct nc_server_tls_opts));
1319 if (!server_opts.endpts[server_opts.endpt_count - 1].tls_opts) {
1320 ERRMEM;
1321 /* WRITE UNLOCK */
1322 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1323 return -1;
1324 }
1325#endif
1326
Michal Vasko3031aae2016-01-27 16:07:18 +01001327 pthread_mutex_init(&server_opts.endpts[server_opts.endpt_count - 1].endpt_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001328
Michal Vasko3031aae2016-01-27 16:07:18 +01001329 /* WRITE UNLOCK */
1330 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001331
Michal Vasko9e036d52016-01-08 10:49:26 +01001332 return 0;
1333}
1334
Michal Vasko3031aae2016-01-27 16:07:18 +01001335int
Michal Vaskoda514772016-02-01 11:32:01 +01001336nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1337{
1338 struct nc_endpt *endpt;
1339 struct nc_bind *bind = NULL;
1340 uint16_t i;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001341 int sock = -1, set_addr;
Michal Vaskoda514772016-02-01 11:32:01 +01001342
Michal Vasko45e53ae2016-04-07 11:46:03 +02001343 if (!endpt_name) {
1344 ERRARG("endpt_name");
1345 return -1;
1346 } else if ((!address && !port) || (address && port)) {
1347 ERRARG("address and port");
1348 return -1;
1349 } else if (!ti) {
1350 ERRARG("ti");
Michal Vaskoda514772016-02-01 11:32:01 +01001351 return -1;
1352 }
1353
Michal Vaskoe2713da2016-08-22 16:06:40 +02001354 if (address) {
1355 set_addr = 1;
1356 } else {
1357 set_addr = 0;
1358 }
1359
Michal Vasko51e514d2016-02-02 15:51:52 +01001360 /* LOCK */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001361 endpt = nc_server_endpt_lock(endpt_name, &i);
Michal Vaskoda514772016-02-01 11:32:01 +01001362 if (!endpt) {
1363 return -1;
1364 }
1365
Michal Vaskoe2713da2016-08-22 16:06:40 +02001366#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1367 if (ti == NC_TI_LIBSSH) {
1368 bind = &server_opts.binds[2 * i];
1369 } else {
1370 bind = &server_opts.binds[2 * i + 1];
Michal Vaskoda514772016-02-01 11:32:01 +01001371 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001372#else
1373 bind = &server_opts.binds[i];
Michal Vasko5e391372016-08-22 16:20:58 +02001374 if (bind->ti != ti) {
1375 ERRINT;
1376 goto fail;
1377 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001378#endif
Michal Vaskoda514772016-02-01 11:32:01 +01001379 if (!bind) {
1380 ERRINT;
Michal Vasko51e514d2016-02-02 15:51:52 +01001381 goto fail;
Michal Vaskoda514772016-02-01 11:32:01 +01001382 }
1383
Michal Vaskoe2713da2016-08-22 16:06:40 +02001384 if (set_addr) {
1385 port = bind->port;
Michal Vaskoda514772016-02-01 11:32:01 +01001386 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001387 address = bind->address;
Michal Vaskoda514772016-02-01 11:32:01 +01001388 }
1389
Michal Vaskoe2713da2016-08-22 16:06:40 +02001390 /* we have all the information we need to create a listening socket */
1391 if (address && port) {
1392 /* create new socket, close the old one */
1393 sock = nc_sock_listen(address, port);
1394 if (sock == -1) {
1395 goto fail;
1396 }
1397
1398 if (bind->sock > -1) {
1399 close(bind->sock);
1400 }
1401 bind->sock = sock;
1402 } /* else we are just setting address or port */
1403
1404 if (set_addr) {
Michal Vaskoda514772016-02-01 11:32:01 +01001405 lydict_remove(server_opts.ctx, bind->address);
1406 bind->address = lydict_insert(server_opts.ctx, address, 0);
1407 } else {
1408 bind->port = port;
1409 }
1410
Michal Vaskoe2713da2016-08-22 16:06:40 +02001411 if (sock > -1) {
1412#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1413 VRB("Listening on %s:%u for %s connections.", address, port, (ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
1414#elif defined(NC_ENABLED_SSH)
1415 VRB("Listening on %s:%u for SSH connections.", address, port);
1416#else
1417 VRB("Listening on %s:%u for TLS connections.", address, port);
1418#endif
1419 }
1420
Michal Vasko51e514d2016-02-02 15:51:52 +01001421 /* UNLOCK */
Michal Vasko7a93af72016-02-01 16:00:15 +01001422 nc_server_endpt_unlock(endpt);
Michal Vaskoda514772016-02-01 11:32:01 +01001423 return 0;
Michal Vasko51e514d2016-02-02 15:51:52 +01001424
1425fail:
1426 /* UNLOCK */
1427 nc_server_endpt_unlock(endpt);
1428 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001429}
1430
Michal Vaskoe2713da2016-08-22 16:06:40 +02001431API int
1432nc_server_del_endpt(const char *name)
Michal Vasko9e036d52016-01-08 10:49:26 +01001433{
1434 uint32_t i;
1435 int ret = -1;
1436
Michal Vasko3031aae2016-01-27 16:07:18 +01001437 /* WRITE LOCK */
1438 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001439
Michal Vaskoe2713da2016-08-22 16:06:40 +02001440 if (!name) {
1441 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001442 for (i = 0; i < server_opts.endpt_count; ++i) {
1443 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko3031aae2016-01-27 16:07:18 +01001444 pthread_mutex_destroy(&server_opts.endpts[i].endpt_lock);
Radek Krejci53691be2016-02-22 13:58:37 +01001445#ifdef NC_ENABLED_SSH
Michal Vaskoe2713da2016-08-22 16:06:40 +02001446 nc_server_ssh_clear_opts(server_opts.endpts[i].ssh_opts);
1447 free(server_opts.endpts[i].ssh_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001448#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001449#ifdef NC_ENABLED_TLS
Michal Vaskoe2713da2016-08-22 16:06:40 +02001450 nc_server_tls_clear_opts(server_opts.endpts[i].tls_opts);
1451 free(server_opts.endpts[i].tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001452#endif
Michal Vasko9e036d52016-01-08 10:49:26 +01001453 ret = 0;
1454 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001455 free(server_opts.endpts);
1456 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001457
1458 /* remove all binds */
1459 for (i = 0; i < server_opts.endpt_count; ++i) {
1460 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1461 if (server_opts.binds[i].sock > -1) {
1462 close(server_opts.binds[i].sock);
1463 }
1464 }
1465#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1466 for (; i < 2 * server_opts.endpt_count; ++i) {
1467 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1468 if (server_opts.binds[i].sock > -1) {
1469 close(server_opts.binds[i].sock);
1470 }
1471 }
1472#endif
1473 free(server_opts.binds);
1474 server_opts.binds = NULL;
1475
Michal Vasko3031aae2016-01-27 16:07:18 +01001476 server_opts.endpt_count = 0;
1477
Michal Vasko1a38c862016-01-15 15:50:07 +01001478 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001479 /* remove one endpoint with bind(s) */
Michal Vasko3031aae2016-01-27 16:07:18 +01001480 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001481 if (!strcmp(server_opts.endpts[i].name, name)) {
1482 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01001483 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko3031aae2016-01-27 16:07:18 +01001484 pthread_mutex_destroy(&server_opts.endpts[i].endpt_lock);
Radek Krejci53691be2016-02-22 13:58:37 +01001485#ifdef NC_ENABLED_SSH
Michal Vaskoe2713da2016-08-22 16:06:40 +02001486 nc_server_ssh_clear_opts(server_opts.endpts[i].ssh_opts);
1487 free(server_opts.endpts[i].ssh_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001488#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001489#ifdef NC_ENABLED_TLS
Michal Vaskoe2713da2016-08-22 16:06:40 +02001490 nc_server_tls_clear_opts(server_opts.endpts[i].tls_opts);
1491 free(server_opts.endpts[i].tls_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001492#endif
Michal Vasko1a38c862016-01-15 15:50:07 +01001493
Michal Vaskoe2713da2016-08-22 16:06:40 +02001494 /* remove bind(s) */
1495#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1496 i *= 2;
1497 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1498 if (server_opts.binds[i].sock > -1) {
1499 close(server_opts.binds[i].sock);
1500 }
1501 ++i;
1502#endif
1503 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1504 if (server_opts.binds[i].sock > -1) {
1505 close(server_opts.binds[i].sock);
1506 }
1507
1508 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01001509 --server_opts.endpt_count;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001510#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1511 --i;
1512 i /= 2;
1513 if (i < server_opts.endpt_count) {
1514 memcpy(&server_opts.binds[2 * i], &server_opts.binds[2 * server_opts.endpt_count], 2 * sizeof *server_opts.binds);
1515 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
1516 }
1517#else
Michal Vaskoc0256492016-02-02 12:19:06 +01001518 if (i < server_opts.endpt_count) {
1519 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1520 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001521 }
1522#endif
1523 else if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01001524 free(server_opts.binds);
1525 server_opts.binds = NULL;
1526 free(server_opts.endpts);
1527 server_opts.endpts = NULL;
1528 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001529
1530 ret = 0;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001531 break;
Michal Vasko1a38c862016-01-15 15:50:07 +01001532 }
1533 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001534 }
1535
Michal Vasko3031aae2016-01-27 16:07:18 +01001536 /* WRITE UNLOCK */
1537 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001538
Michal Vasko9e036d52016-01-08 10:49:26 +01001539 return ret;
1540}
1541
Michal Vasko71090fc2016-05-24 16:37:28 +02001542API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01001543nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001544{
Michal Vasko71090fc2016-05-24 16:37:28 +02001545 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01001546 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01001547 char *host = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001548 uint16_t port, endpt_idx, bind_idx;
Michal Vasko9e036d52016-01-08 10:49:26 +01001549
Michal Vasko45e53ae2016-04-07 11:46:03 +02001550 if (!server_opts.ctx) {
1551 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001552 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001553 } else if (!session) {
1554 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001555 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001556 }
1557
Michal Vasko51e514d2016-02-02 15:51:52 +01001558 /* we have to hold WRITE for the whole time, since there is not
1559 * a way of downgrading the lock to READ */
1560 /* WRITE LOCK */
1561 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
1562
1563 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02001564 ERR("No endpoints to accept sessions on.");
Michal Vasko51e514d2016-02-02 15:51:52 +01001565 /* WRITE UNLOCK */
1566 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001567 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01001568 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001569
Michal Vaskoe2713da2016-08-22 16:06:40 +02001570 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vaskob48aa812016-01-18 14:13:09 +01001571
Michal Vasko50456e82016-02-02 12:16:08 +01001572 if (ret < 1) {
Michal Vasko51e514d2016-02-02 15:51:52 +01001573 /* WRITE UNLOCK */
Michal Vasko3031aae2016-01-27 16:07:18 +01001574 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01001575 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02001576 if (!ret) {
1577 return NC_MSG_WOULDBLOCK;
1578 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001579 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001580 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001581 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001582
Michal Vasko1a38c862016-01-15 15:50:07 +01001583 *session = calloc(1, sizeof **session);
Michal Vasko686aa312016-01-21 15:58:18 +01001584 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001585 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001586 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01001587 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02001588 msgtype = NC_MSG_ERROR;
1589 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001590 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001591 (*session)->status = NC_STATUS_STARTING;
1592 (*session)->side = NC_SERVER;
1593 (*session)->ctx = server_opts.ctx;
1594 (*session)->flags = NC_SESSION_SHAREDCTX;
1595 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
1596 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01001597
1598 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +01001599 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1600 if (!(*session)->ti_lock) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001601 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001602 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001603 msgtype = NC_MSG_ERROR;
1604 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001605 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001606 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001607
Michal Vaskoe2713da2016-08-22 16:06:40 +02001608 endpt_idx = bind_idx;
1609 /* transform index as needed */
1610#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
1611 if (server_opts.binds[bind_idx].ti == NC_TI_OPENSSL) {
1612 --endpt_idx;
1613 }
1614 endpt_idx /= 2;
1615#endif
Michal Vasko3031aae2016-01-27 16:07:18 +01001616
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001617 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001618#ifdef NC_ENABLED_SSH
Michal Vaskoe2713da2016-08-22 16:06:40 +02001619 if (server_opts.binds[bind_idx].ti == NC_TI_LIBSSH) {
1620 (*session)->data = server_opts.endpts[endpt_idx].ssh_opts;
Michal Vasko0190bc32016-03-02 15:47:49 +01001621 ret = nc_accept_ssh_session(*session, sock, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001622 if (ret < 0) {
1623 msgtype = NC_MSG_ERROR;
1624 goto cleanup;
1625 } else if (!ret) {
1626 msgtype = NC_MSG_WOULDBLOCK;
1627 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001628 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001629 } else
1630#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001631#ifdef NC_ENABLED_TLS
Michal Vaskoe2713da2016-08-22 16:06:40 +02001632 if (server_opts.binds[bind_idx].ti == NC_TI_OPENSSL) {
1633 (*session)->data = server_opts.endpts[endpt_idx].tls_opts;
Michal Vasko0190bc32016-03-02 15:47:49 +01001634 ret = nc_accept_tls_session(*session, sock, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001635 if (ret < 0) {
1636 msgtype = NC_MSG_ERROR;
1637 goto cleanup;
1638 } else if (!ret) {
1639 msgtype = NC_MSG_WOULDBLOCK;
1640 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001641 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001642 } else
1643#endif
1644 {
Michal Vasko9e036d52016-01-08 10:49:26 +01001645 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001646 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001647 msgtype = NC_MSG_ERROR;
1648 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001649 }
1650
Michal Vasko2cc4c682016-03-01 09:16:48 +01001651 (*session)->data = NULL;
1652
Michal Vasko51e514d2016-02-02 15:51:52 +01001653 /* WRITE UNLOCK */
Michal Vasko3031aae2016-01-27 16:07:18 +01001654 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1655
Michal Vaskob48aa812016-01-18 14:13:09 +01001656 /* assign new SID atomically */
1657 /* LOCK */
1658 pthread_spin_lock(&server_opts.sid_lock);
1659 (*session)->id = server_opts.new_session_id++;
1660 /* UNLOCK */
1661 pthread_spin_unlock(&server_opts.sid_lock);
1662
Michal Vasko9e036d52016-01-08 10:49:26 +01001663 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001664 msgtype = nc_handshake(*session);
1665 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001666 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001667 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001668 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001669 }
Michal Vasko2f975cf2016-07-28 15:47:00 +02001670 (*session)->session_start = (*session)->last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001671 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01001672
Michal Vasko71090fc2016-05-24 16:37:28 +02001673 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001674
Michal Vasko71090fc2016-05-24 16:37:28 +02001675cleanup:
Michal Vasko3031aae2016-01-27 16:07:18 +01001676 /* WRITE UNLOCK */
1677 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1678
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001679 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001680 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001681 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001682}
1683
Michal Vasko71090fc2016-05-24 16:37:28 +02001684NC_MSG_TYPE
Michal Vasko8f5270d2016-02-29 16:22:25 +01001685nc_connect_callhome(const char *host, uint16_t port, NC_TRANSPORT_IMPL ti, struct nc_session **session)
Michal Vaskob05053d2016-01-22 16:12:06 +01001686{
Michal Vasko71090fc2016-05-24 16:37:28 +02001687 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01001688 int sock, ret;
1689
Michal Vasko45e53ae2016-04-07 11:46:03 +02001690 if (!host) {
1691 ERRARG("host");
Michal Vasko71090fc2016-05-24 16:37:28 +02001692 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001693 } else if (!port) {
1694 ERRARG("port");
Michal Vasko71090fc2016-05-24 16:37:28 +02001695 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001696 } else if (!ti) {
1697 ERRARG("ti");
Michal Vasko71090fc2016-05-24 16:37:28 +02001698 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001699 } else if (!session) {
1700 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001701 return NC_MSG_ERROR;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001702 }
1703
Michal Vaskob05053d2016-01-22 16:12:06 +01001704 sock = nc_sock_connect(host, port);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001705 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001706 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01001707 }
1708
1709 *session = calloc(1, sizeof **session);
1710 if (!(*session)) {
1711 ERRMEM;
1712 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001713 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01001714 }
1715 (*session)->status = NC_STATUS_STARTING;
1716 (*session)->side = NC_SERVER;
1717 (*session)->ctx = server_opts.ctx;
1718 (*session)->flags = NC_SESSION_SHAREDCTX | NC_SESSION_CALLHOME;
Michal Vaskob05053d2016-01-22 16:12:06 +01001719 (*session)->host = lydict_insert(server_opts.ctx, host, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01001720 (*session)->port = port;
1721
1722 /* transport lock */
1723 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1724 if (!(*session)->ti_lock) {
1725 ERRMEM;
1726 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001727 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01001728 goto fail;
1729 }
1730 pthread_mutex_init((*session)->ti_lock, NULL);
1731
1732 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001733#ifdef NC_ENABLED_SSH
Michal Vaskob05053d2016-01-22 16:12:06 +01001734 if (ti == NC_TI_LIBSSH) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001735 /* OPTS LOCK */
1736 pthread_mutex_lock(&ssh_ch_opts_lock);
1737
Michal Vasko2cc4c682016-03-01 09:16:48 +01001738 (*session)->data = &ssh_ch_opts;
Michal Vasko0190bc32016-03-02 15:47:49 +01001739 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01001740 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001741
1742 /* OPTS UNLOCK */
1743 pthread_mutex_unlock(&ssh_ch_opts_lock);
1744
Michal Vasko71090fc2016-05-24 16:37:28 +02001745 if (ret < 0) {
1746 msgtype = NC_MSG_ERROR;
1747 goto fail;
1748 } else if (!ret) {
1749 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01001750 goto fail;
1751 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001752 } else
1753#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001754#ifdef NC_ENABLED_TLS
Michal Vasko3d865d22016-01-28 16:00:53 +01001755 if (ti == NC_TI_OPENSSL) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001756 /* OPTS LOCK */
1757 pthread_mutex_lock(&tls_ch_opts_lock);
1758
Michal Vasko2cc4c682016-03-01 09:16:48 +01001759 (*session)->data = &tls_ch_opts;
Michal Vasko0190bc32016-03-02 15:47:49 +01001760 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01001761 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001762
1763 /* OPTS UNLOCK */
1764 pthread_mutex_unlock(&tls_ch_opts_lock);
1765
Michal Vasko71090fc2016-05-24 16:37:28 +02001766 if (ret < 0) {
1767 msgtype = NC_MSG_ERROR;
1768 goto fail;
1769 } else if (!ret) {
1770 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01001771 goto fail;
1772 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001773 } else
1774#endif
1775 {
Michal Vaskob05053d2016-01-22 16:12:06 +01001776 ERRINT;
1777 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001778 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01001779 goto fail;
1780 }
1781
1782 /* assign new SID atomically */
1783 /* LOCK */
1784 pthread_spin_lock(&server_opts.sid_lock);
1785 (*session)->id = server_opts.new_session_id++;
1786 /* UNLOCK */
1787 pthread_spin_unlock(&server_opts.sid_lock);
1788
1789 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001790 msgtype = nc_handshake(*session);
1791 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01001792 goto fail;
1793 }
Michal Vasko2f975cf2016-07-28 15:47:00 +02001794 (*session)->session_start = (*session)->last_rpc = time(NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01001795 (*session)->status = NC_STATUS_RUNNING;
1796
Michal Vasko71090fc2016-05-24 16:37:28 +02001797 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01001798
1799fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001800 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01001801 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001802 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01001803}
1804
Radek Krejci53691be2016-02-22 13:58:37 +01001805#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02001806
Michal Vaskoc45ebd32016-05-25 11:17:36 +02001807API time_t
1808nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02001809{
1810 if (!session) {
1811 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02001812 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02001813 }
1814
Michal Vaskoc45ebd32016-05-25 11:17:36 +02001815 return session->session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02001816}