blob: 1894cae865730e11a5252507e31466ea70d41706 [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 Vaskoc6b9c7b2016-01-28 11:10:08 +010043nc_server_endpt_lock(const char *name, NC_TRANSPORT_IMPL ti)
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) {
52 if ((server_opts.binds[i].ti == ti) && !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
68 return endpt;
69}
70
71void
72nc_server_endpt_unlock(struct nc_endpt *endpt)
73{
74 /* ENDPT UNLOCK */
75 pthread_mutex_unlock(&endpt->endpt_lock);
76
77 /* READ UNLOCK */
Michal Vasko27562ad2016-02-02 15:50:39 +010078 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010079}
Michal Vasko086311b2016-01-08 09:53:11 +010080
Michal Vasko1a38c862016-01-15 15:50:07 +010081API void
82nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
83{
Michal Vasko45e53ae2016-04-07 11:46:03 +020084 if (!session) {
85 ERRARG("session");
86 return;
87 } else if (!reason) {
88 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +010089 return;
90 }
91
92 session->term_reason = reason;
93}
94
Michal Vasko086311b2016-01-08 09:53:11 +010095int
Michal Vaskof05562c2016-01-20 12:06:43 +010096nc_sock_listen(const char *address, uint16_t port)
Michal Vasko086311b2016-01-08 09:53:11 +010097{
98 const int optVal = 1;
99 const socklen_t optLen = sizeof(optVal);
100 int is_ipv4, sock;
101 struct sockaddr_storage saddr;
102
103 struct sockaddr_in *saddr4;
104 struct sockaddr_in6 *saddr6;
105
106
107 if (!strchr(address, ':')) {
108 is_ipv4 = 1;
109 } else {
110 is_ipv4 = 0;
111 }
112
113 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
114 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100115 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100116 goto fail;
117 }
118
119 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&optVal, optLen)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100120 ERR("Could not set socket SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100121 goto fail;
122 }
123
124 bzero(&saddr, sizeof(struct sockaddr_storage));
125 if (is_ipv4) {
126 saddr4 = (struct sockaddr_in *)&saddr;
127
128 saddr4->sin_family = AF_INET;
129 saddr4->sin_port = htons(port);
130
131 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100132 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100133 goto fail;
134 }
135
136 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100137 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100138 goto fail;
139 }
140
141 } else {
142 saddr6 = (struct sockaddr_in6 *)&saddr;
143
144 saddr6->sin6_family = AF_INET6;
145 saddr6->sin6_port = htons(port);
146
147 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100148 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100149 goto fail;
150 }
151
152 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100153 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100154 goto fail;
155 }
156 }
157
Michal Vaskofb89d772016-01-08 12:25:35 +0100158 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100159 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100160 goto fail;
161 }
162
163 return sock;
164
165fail:
166 if (sock > -1) {
167 close(sock);
168 }
169
170 return -1;
171}
172
173int
Michal Vasko3031aae2016-01-27 16:07:18 +0100174nc_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 +0100175{
176 uint16_t i;
177 struct pollfd *pfd;
178 struct sockaddr_storage saddr;
179 socklen_t saddr_len = sizeof(saddr);
Michal Vasko0190bc32016-03-02 15:47:49 +0100180 int ret, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100181
182 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100183 if (!pfd) {
184 ERRMEM;
185 return -1;
186 }
187
Michal Vasko086311b2016-01-08 09:53:11 +0100188 for (i = 0; i < bind_count; ++i) {
189 pfd[i].fd = binds[i].sock;
190 pfd[i].events = POLLIN;
191 pfd[i].revents = 0;
192 }
193
194 /* poll for a new connection */
195 errno = 0;
196 ret = poll(pfd, bind_count, timeout);
197 if (!ret) {
198 /* we timeouted */
199 free(pfd);
200 return 0;
201 } else if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100202 ERR("Poll failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100203 free(pfd);
204 return -1;
205 }
206
207 for (i = 0; i < bind_count; ++i) {
208 if (pfd[i].revents & POLLIN) {
209 sock = pfd[i].fd;
210 break;
211 }
212 }
213 free(pfd);
214
215 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100216 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100217 return -1;
218 }
219
220 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100221 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100222 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100223 return -1;
224 }
225
Michal Vasko0190bc32016-03-02 15:47:49 +0100226 /* make the socket non-blocking */
227 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
228 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100229 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100230 return -1;
231 }
232
Michal Vasko3031aae2016-01-27 16:07:18 +0100233 if (idx) {
234 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100235 }
236
Michal Vasko086311b2016-01-08 09:53:11 +0100237 /* host was requested */
238 if (host) {
239 if (saddr.ss_family == AF_INET) {
240 *host = malloc(15);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100241 if (*host) {
242 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, 15)) {
243 ERR("inet_ntop failed (%s).", strerror(errno));
244 free(*host);
245 *host = NULL;
246 }
Michal Vasko086311b2016-01-08 09:53:11 +0100247
Michal Vasko4eb3c312016-03-01 14:09:37 +0100248 if (port) {
249 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
250 }
251 } else {
252 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100253 }
254 } else if (saddr.ss_family == AF_INET6) {
255 *host = malloc(40);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100256 if (*host) {
257 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, 40)) {
258 ERR("inet_ntop failed (%s).", strerror(errno));
259 free(*host);
260 *host = NULL;
261 }
Michal Vasko086311b2016-01-08 09:53:11 +0100262
Michal Vasko4eb3c312016-03-01 14:09:37 +0100263 if (port) {
264 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
265 }
266 } else {
267 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100268 }
269 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100270 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100271 }
272 }
273
274 return ret;
275}
276
Michal Vasko05ba9df2016-01-13 14:40:27 +0100277static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100278nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100279{
280 const char *identifier = NULL, *version = NULL, *format = NULL;
281 char *model_data = NULL;
282 const struct lys_module *module;
283 struct nc_server_error *err;
284 struct lyd_node *child, *data = NULL;
Michal Vasko11d142a2016-01-19 15:58:24 +0100285 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100286
287 LY_TREE_FOR(rpc->child, child) {
288 if (!strcmp(child->schema->name, "identifier")) {
289 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
290 } else if (!strcmp(child->schema->name, "version")) {
291 version = ((struct lyd_node_leaf_list *)child)->value_str;
292 } else if (!strcmp(child->schema->name, "format")) {
293 format = ((struct lyd_node_leaf_list *)child)->value_str;
294 }
295 }
296
297 /* check version */
298 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100299 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
300 nc_err_set_msg(err, "The requested version is not supported.", "en");
301 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100302 }
303
304 /* check and get module with the name identifier */
305 module = ly_ctx_get_module(server_opts.ctx, identifier, version);
306 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200307 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
308 }
309 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100310 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
311 nc_err_set_msg(err, "The requested schema was not found.", "en");
312 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100313 }
314
315 /* check format */
316 if (!format || !strcmp(format, "yang")) {
317 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL);
318 } else if (!strcmp(format, "yin")) {
319 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL);
320 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100321 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
322 nc_err_set_msg(err, "The requested format is not supported.", "en");
323 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100324 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200325 if (!model_data) {
326 ERRINT;
327 return NULL;
328 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100329
Michal Vasko303245c2016-03-24 15:20:03 +0100330 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/output/data");
Michal Vaskod91f6e62016-04-05 11:34:22 +0200331 if (!sdata) {
332 ERRINT;
333 free(model_data);
334 return NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100335 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200336
Michal Vaskob2583f12016-05-12 11:40:23 +0200337 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 +0100338 if (!data) {
339 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200340 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100341 return NULL;
342 }
Michal Vaskob2583f12016-05-12 11:40:23 +0200343 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100344
345 return nc_server_reply_data(data, NC_PARAMTYPE_FREE);
346}
347
348static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100349nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100350{
Michal Vasko428087d2016-01-14 16:04:28 +0100351 session->term_reason = NC_SESSION_TERM_CLOSED;
352 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100353}
354
Michal Vasko086311b2016-01-08 09:53:11 +0100355API int
356nc_server_init(struct ly_ctx *ctx)
357{
Michal Vasko05ba9df2016-01-13 14:40:27 +0100358 const struct lys_node *rpc;
359
Michal Vasko086311b2016-01-08 09:53:11 +0100360 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200361 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100362 return -1;
363 }
364
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100365 nc_init();
366
Michal Vasko05ba9df2016-01-13 14:40:27 +0100367 /* set default <get-schema> callback if not specified */
Michal Vasko303245c2016-03-24 15:20:03 +0100368 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema");
Michal Vaskofd100c92016-03-01 15:23:46 +0100369 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100370 lys_set_private(rpc, nc_clb_default_get_schema);
371 }
372
373 /* set default <close-session> callback if not specififed */
Michal Vasko303245c2016-03-24 15:20:03 +0100374 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session");
Michal Vaskofd100c92016-03-01 15:23:46 +0100375 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100376 lys_set_private(rpc, nc_clb_default_close_session);
377 }
378
Michal Vasko086311b2016-01-08 09:53:11 +0100379 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100380
381 server_opts.new_session_id = 1;
382 pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
383
Michal Vasko086311b2016-01-08 09:53:11 +0100384 return 0;
385}
386
Michal Vaskob48aa812016-01-18 14:13:09 +0100387API void
388nc_server_destroy(void)
389{
390 pthread_spin_destroy(&server_opts.sid_lock);
391
Radek Krejci53691be2016-02-22 13:58:37 +0100392#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko3031aae2016-01-27 16:07:18 +0100393 nc_server_del_endpt(NULL, 0);
Michal Vaskob48aa812016-01-18 14:13:09 +0100394#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100395 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100396}
397
Michal Vasko086311b2016-01-08 09:53:11 +0100398API int
399nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
400{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200401 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
402 ERRARG("basic_mode");
403 return -1;
404 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
405 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100406 return -1;
407 }
408
409 server_opts.wd_basic_mode = basic_mode;
410 server_opts.wd_also_supported = also_supported;
411 return 0;
412}
413
Michal Vasko1a38c862016-01-15 15:50:07 +0100414API void
Michal Vasko55f03972016-04-13 08:56:01 +0200415nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
416{
417 if (!basic_mode && !also_supported) {
418 ERRARG("basic_mode and also_supported");
419 return;
420 }
421
422 if (basic_mode) {
423 *basic_mode = server_opts.wd_basic_mode;
424 }
425 if (also_supported) {
426 *also_supported = server_opts.wd_also_supported;
427 }
428}
429
430API void
Michal Vasko086311b2016-01-08 09:53:11 +0100431nc_server_set_capab_interleave(int interleave_support)
432{
433 if (interleave_support) {
434 server_opts.interleave_capab = 1;
435 } else {
436 server_opts.interleave_capab = 0;
437 }
Michal Vasko086311b2016-01-08 09:53:11 +0100438}
439
Michal Vasko55f03972016-04-13 08:56:01 +0200440API int
441nc_server_get_capab_interleave(void)
442{
443 return server_opts.interleave_capab;
444}
445
Michal Vasko1a38c862016-01-15 15:50:07 +0100446API void
Michal Vasko086311b2016-01-08 09:53:11 +0100447nc_server_set_hello_timeout(uint16_t hello_timeout)
448{
Michal Vasko086311b2016-01-08 09:53:11 +0100449 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100450}
451
Michal Vasko55f03972016-04-13 08:56:01 +0200452API uint16_t
453nc_server_get_hello_timeout(void)
454{
455 return server_opts.hello_timeout;
456}
457
Michal Vasko1a38c862016-01-15 15:50:07 +0100458API void
Michal Vasko086311b2016-01-08 09:53:11 +0100459nc_server_set_idle_timeout(uint16_t idle_timeout)
460{
Michal Vasko086311b2016-01-08 09:53:11 +0100461 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100462}
463
Michal Vasko55f03972016-04-13 08:56:01 +0200464API uint16_t
465nc_server_get_idle_timeout(void)
466{
467 return server_opts.idle_timeout;
468}
469
Michal Vasko71090fc2016-05-24 16:37:28 +0200470API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100471nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100472{
Michal Vasko71090fc2016-05-24 16:37:28 +0200473 NC_MSG_TYPE msgtype;
474
Michal Vasko45e53ae2016-04-07 11:46:03 +0200475 if (!server_opts.ctx) {
476 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200477 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200478 } else if (fdin < 0) {
479 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200480 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200481 } else if (fdout < 0) {
482 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200483 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200484 } else if (!username) {
485 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200486 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200487 } else if (!session) {
488 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200489 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100490 }
491
492 /* prepare session structure */
Michal Vasko1a38c862016-01-15 15:50:07 +0100493 *session = calloc(1, sizeof **session);
494 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100495 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200496 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100497 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100498 (*session)->status = NC_STATUS_STARTING;
499 (*session)->side = NC_SERVER;
Michal Vasko086311b2016-01-08 09:53:11 +0100500
501 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100502 (*session)->ti_type = NC_TI_FD;
503 (*session)->ti.fd.in = fdin;
504 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100505
506 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100507 (*session)->flags = NC_SESSION_SHAREDCTX;
508 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100509
Michal Vaskob48aa812016-01-18 14:13:09 +0100510 /* assign new SID atomically */
511 pthread_spin_lock(&server_opts.sid_lock);
512 (*session)->id = server_opts.new_session_id++;
513 pthread_spin_unlock(&server_opts.sid_lock);
514
Michal Vasko086311b2016-01-08 09:53:11 +0100515 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200516 msgtype = nc_handshake(*session);
517 if (msgtype != NC_MSG_HELLO) {
518 nc_session_free(*session, NULL);
519 *session = NULL;
520 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100521 }
Michal Vaskof8352352016-05-24 09:11:36 +0200522 (*session)->session_start = (*session)->last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +0100523 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100524
Michal Vasko71090fc2016-05-24 16:37:28 +0200525 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100526}
Michal Vasko9e036d52016-01-08 10:49:26 +0100527
Michal Vaskobdcf2362016-07-26 11:35:43 +0200528static void
529nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
530{
531 uint8_t i, found = 0;
532
533 for (i = 0; i < ps->queue_len; ++i) {
534 /* idx round buffer adjust */
535 if (ps->queue_begin + i == NC_PS_QUEUE_SIZE) {
536 i = -ps->queue_begin;
537 }
538
539 if (found) {
540 /* move the value back one place */
541 if (ps->queue[ps->queue_begin + i] == id) {
542 /* another equal value, simply cannot be */
543 ERRINT;
544 }
545
546 if (ps->queue_begin + i == 0) {
547 ps->queue[NC_PS_QUEUE_SIZE - 1] = ps->queue[ps->queue_begin + i];
548 } else {
549 ps->queue[ps->queue_begin + i - 1] = ps->queue[ps->queue_begin + i];
550 }
551 } else if (ps->queue[ps->queue_begin + i] == id) {
552 /* found our id, there can be no more equal valid values */
553 found = 1;
554 }
555 }
556
557 if (!found) {
558 ERRINT;
559 }
560 --ps->queue_len;
561}
562
Michal Vaskof04a52a2016-04-07 10:52:10 +0200563int
Michal Vasko227f8ff2016-07-26 14:08:59 +0200564nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200565{
566 int ret;
Michal Vaskobdcf2362016-07-26 11:35:43 +0200567 uint8_t queue_last;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200568 struct timespec ts;
569
Radek Krejci7ac16052016-07-15 11:48:18 +0200570 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200571 ts.tv_sec += NC_READ_TIMEOUT;
572
573 /* LOCK */
574 ret = pthread_mutex_timedlock(&ps->lock, &ts);
575 if (ret) {
Michal Vasko227f8ff2016-07-26 14:08:59 +0200576 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200577 return -1;
578 }
579
580 /* get a unique queue value (by adding 1 to the last added value, if any) */
581 if (ps->queue_len) {
582 queue_last = ps->queue_begin + ps->queue_len - 1;
583 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
584 queue_last -= NC_PS_QUEUE_SIZE;
585 }
Michal Vaskobdcf2362016-07-26 11:35:43 +0200586 *id = ps->queue[queue_last] + 1;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200587 } else {
Michal Vaskobdcf2362016-07-26 11:35:43 +0200588 *id = 0;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200589 }
590
591 /* add ourselves into the queue */
592 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko227f8ff2016-07-26 14:08:59 +0200593 ERR("%s: pollsession queue too small.", func);
Michal Vasko8c242532016-07-26 12:23:24 +0200594 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200595 return -1;
596 }
597 ++ps->queue_len;
598 queue_last = ps->queue_begin + ps->queue_len - 1;
599 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
600 queue_last -= NC_PS_QUEUE_SIZE;
601 }
Michal Vaskobdcf2362016-07-26 11:35:43 +0200602 ps->queue[queue_last] = *id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200603
604 /* is it our turn? */
Michal Vaskobdcf2362016-07-26 11:35:43 +0200605 while (ps->queue[ps->queue_begin] != *id) {
Radek Krejci7ac16052016-07-15 11:48:18 +0200606 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200607 ts.tv_sec += NC_READ_TIMEOUT;
608
609 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
610 if (ret) {
Michal Vasko227f8ff2016-07-26 14:08:59 +0200611 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200612 /* remove ourselves from the queue */
Michal Vaskobdcf2362016-07-26 11:35:43 +0200613 nc_ps_queue_remove_id(ps, *id);
614 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200615 return -1;
616 }
617 }
618
Michal Vaskobe86fe32016-04-07 10:43:03 +0200619 /* UNLOCK */
620 pthread_mutex_unlock(&ps->lock);
621
622 return 0;
623}
624
Michal Vaskof04a52a2016-04-07 10:52:10 +0200625int
Michal Vasko227f8ff2016-07-26 14:08:59 +0200626nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200627{
628 int ret;
629 struct timespec ts;
630
Radek Krejci7ac16052016-07-15 11:48:18 +0200631 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200632 ts.tv_sec += NC_READ_TIMEOUT;
633
634 /* LOCK */
635 ret = pthread_mutex_timedlock(&ps->lock, &ts);
636 if (ret) {
Michal Vasko227f8ff2016-07-26 14:08:59 +0200637 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200638 ret = -1;
639 }
640
Michal Vaskobdcf2362016-07-26 11:35:43 +0200641 /* we must be the first, it was our turn after all, right? */
642 if (ps->queue[ps->queue_begin] != id) {
643 ERRINT;
644 return -1;
645 }
646
Michal Vaskobe86fe32016-04-07 10:43:03 +0200647 /* remove ourselves from the queue */
Michal Vaskobdcf2362016-07-26 11:35:43 +0200648 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200649
650 /* broadcast to all other threads that the queue moved */
651 pthread_cond_broadcast(&ps->cond);
652
Michal Vaskobe86fe32016-04-07 10:43:03 +0200653 /* UNLOCK */
654 if (!ret) {
655 pthread_mutex_unlock(&ps->lock);
656 }
657
658 return ret;
659}
660
Michal Vasko428087d2016-01-14 16:04:28 +0100661API struct nc_pollsession *
662nc_ps_new(void)
663{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100664 struct nc_pollsession *ps;
665
666 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100667 if (!ps) {
668 ERRMEM;
669 return NULL;
670 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200671 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100672 pthread_mutex_init(&ps->lock, NULL);
673
674 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100675}
676
677API void
678nc_ps_free(struct nc_pollsession *ps)
679{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100680 if (!ps) {
681 return;
682 }
683
Michal Vaskobe86fe32016-04-07 10:43:03 +0200684 if (ps->queue_len) {
685 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
686 }
687
Michal Vasko3a715132016-01-21 15:40:31 +0100688 free(ps->pfds);
Michal Vasko428087d2016-01-14 16:04:28 +0100689 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100690 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200691 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100692
Michal Vasko428087d2016-01-14 16:04:28 +0100693 free(ps);
694}
695
696API int
697nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
698{
Michal Vaskobdcf2362016-07-26 11:35:43 +0200699 uint8_t q_id;
700
Michal Vasko45e53ae2016-04-07 11:46:03 +0200701 if (!ps) {
702 ERRARG("ps");
703 return -1;
704 } else if (!session) {
705 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +0100706 return -1;
707 }
708
Michal Vasko48a63ed2016-03-01 09:48:21 +0100709 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +0200710 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200711 return -1;
712 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100713
Michal Vasko428087d2016-01-14 16:04:28 +0100714 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100715 ps->pfds = nc_realloc(ps->pfds, ps->session_count * sizeof *ps->pfds);
716 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
717 if (!ps->pfds || !ps->sessions) {
718 ERRMEM;
719 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +0200720 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100721 return -1;
722 }
Michal Vasko428087d2016-01-14 16:04:28 +0100723
724 switch (session->ti_type) {
725 case NC_TI_FD:
Michal Vasko3a715132016-01-21 15:40:31 +0100726 ps->pfds[ps->session_count - 1].fd = session->ti.fd.in;
Michal Vasko428087d2016-01-14 16:04:28 +0100727 break;
728
Radek Krejci53691be2016-02-22 13:58:37 +0100729#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100730 case NC_TI_LIBSSH:
Michal Vasko3a715132016-01-21 15:40:31 +0100731 ps->pfds[ps->session_count - 1].fd = ssh_get_fd(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100732 break;
733#endif
734
Radek Krejci53691be2016-02-22 13:58:37 +0100735#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100736 case NC_TI_OPENSSL:
Michal Vasko3a715132016-01-21 15:40:31 +0100737 ps->pfds[ps->session_count - 1].fd = SSL_get_rfd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100738 break;
739#endif
740
741 default:
742 ERRINT;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100743 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +0200744 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100745 return -1;
746 }
Michal Vasko3a715132016-01-21 15:40:31 +0100747 ps->pfds[ps->session_count - 1].events = POLLIN;
748 ps->pfds[ps->session_count - 1].revents = 0;
749 ps->sessions[ps->session_count - 1] = session;
Michal Vasko428087d2016-01-14 16:04:28 +0100750
Michal Vasko48a63ed2016-03-01 09:48:21 +0100751 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +0200752 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100753}
754
Michal Vasko48a63ed2016-03-01 09:48:21 +0100755static int
Radek Krejcid5f978f2016-03-03 13:14:45 +0100756_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +0100757{
758 uint16_t i;
759
Radek Krejcid5f978f2016-03-03 13:14:45 +0100760 if (index >= 0) {
761 i = (uint16_t)index;
762 goto remove;
763 }
Michal Vasko428087d2016-01-14 16:04:28 +0100764 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100765 if (ps->sessions[i] == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +0100766remove:
Michal Vasko428087d2016-01-14 16:04:28 +0100767 --ps->session_count;
Michal Vasko58005732016-02-02 15:50:52 +0100768 if (i < ps->session_count) {
769 ps->sessions[i] = ps->sessions[ps->session_count];
770 memcpy(&ps->pfds[i], &ps->pfds[ps->session_count], sizeof *ps->pfds);
771 } else if (!ps->session_count) {
772 free(ps->sessions);
773 ps->sessions = NULL;
774 free(ps->pfds);
775 ps->pfds = NULL;
776 }
Michal Vasko428087d2016-01-14 16:04:28 +0100777 return 0;
778 }
779 }
780
Michal Vaskof0537d82016-01-29 14:42:38 +0100781 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +0100782}
783
Michal Vasko48a63ed2016-03-01 09:48:21 +0100784API int
785nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
786{
Michal Vaskobdcf2362016-07-26 11:35:43 +0200787 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200788 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100789
Michal Vasko45e53ae2016-04-07 11:46:03 +0200790 if (!ps) {
791 ERRARG("ps");
792 return -1;
793 } else if (!session) {
794 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +0100795 return -1;
796 }
797
798 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +0200799 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200800 return -1;
801 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100802
Radek Krejcid5f978f2016-03-03 13:14:45 +0100803 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100804
805 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +0200806 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100807
Michal Vaskobe86fe32016-04-07 10:43:03 +0200808 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100809}
810
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100811API uint16_t
812nc_ps_session_count(struct nc_pollsession *ps)
813{
Michal Vaskobdcf2362016-07-26 11:35:43 +0200814 uint8_t q_id;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100815 uint16_t count;
816
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100817 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200818 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100819 return 0;
820 }
821
Michal Vasko48a63ed2016-03-01 09:48:21 +0100822 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +0200823 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200824 return -1;
825 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100826
827 count = ps->session_count;
828
829 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +0200830 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100831
832 return count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100833}
834
Michal Vasko71090fc2016-05-24 16:37:28 +0200835/* must be called holding the session lock!
836 * returns: NC_PSPOLL_ERROR,
837 * NC_PSPOLL_BAD_RPC,
838 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
839 * NC_PSPOLL_RPC
840 */
841static int
Michal Vasko428087d2016-01-14 16:04:28 +0100842nc_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
843{
844 struct lyxml_elem *xml = NULL;
845 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200846 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200847 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100848
Michal Vasko45e53ae2016-04-07 11:46:03 +0200849 if (!session) {
850 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200851 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200852 } else if (!rpc) {
853 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +0200854 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100855 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100856 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200857 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100858 }
859
860 msgtype = nc_read_msg(session, &xml);
861
862 switch (msgtype) {
863 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +0200864 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100865 if (!*rpc) {
866 ERRMEM;
867 goto error;
868 }
Michal Vaskoca4a2422016-02-02 12:17:14 +0100869
Radek Krejcif93c7d42016-04-06 13:41:15 +0200870 ly_errno = LY_SUCCESS;
Michal Vasko428087d2016-01-14 16:04:28 +0100871 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child, LYD_OPT_DESTRUCT | LYD_OPT_RPC);
Michal Vaskoca4a2422016-02-02 12:17:14 +0100872 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +0200873 /* parsing RPC failed */
Radek Krejci877e1822016-04-06 16:37:43 +0200874 reply = nc_server_reply_err(nc_err_libyang());
Radek Krejci844662e2016-04-13 16:54:43 +0200875 ret = nc_write_msg(session, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200876 nc_server_reply_free(reply);
877 if (ret == -1) {
878 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200879 }
Michal Vasko71090fc2016-05-24 16:37:28 +0200880 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
881 } else {
882 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +0100883 }
Michal Vasko428087d2016-01-14 16:04:28 +0100884 (*rpc)->root = xml;
885 break;
886 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +0100887 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200888 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100889 goto error;
890 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +0100891 ERR("Session %u: received <rpc-reply> from a NETCONF client.", 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_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +0100895 ERR("Session %u: received <notification> 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 default:
Michal Vasko71090fc2016-05-24 16:37:28 +0200899 /* NC_MSG_ERROR,
Michal Vasko428087d2016-01-14 16:04:28 +0100900 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
901 */
Michal Vasko71090fc2016-05-24 16:37:28 +0200902 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100903 break;
904 }
905
Michal Vasko71090fc2016-05-24 16:37:28 +0200906 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100907
908error:
909 /* cleanup */
910 lyxml_free(server_opts.ctx, xml);
911
Michal Vasko71090fc2016-05-24 16:37:28 +0200912 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100913}
914
Michal Vasko71090fc2016-05-24 16:37:28 +0200915/* must be called holding the session lock!
916 * returns: NC_PSPOLL_ERROR,
917 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
918 * NC_PSPOLL_REPLY_ERROR,
919 * 0
920 */
921static int
Michal Vasko428087d2016-01-14 16:04:28 +0100922nc_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
923{
924 nc_rpc_clb clb;
925 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +0200926 struct lys_node *rpc_act = NULL;
927 struct lyd_node *next, *elem;
Michal Vasko71090fc2016-05-24 16:37:28 +0200928 int ret = 0, r;
Michal Vasko428087d2016-01-14 16:04:28 +0100929
Michal Vasko4a827e52016-03-03 10:59:00 +0100930 if (!rpc) {
931 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200932 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +0100933 }
934
Michal Vasko90e8e692016-07-13 12:27:57 +0200935 if (rpc->tree->schema->nodetype == LYS_RPC) {
936 /* RPC */
937 rpc_act = rpc->tree->schema;
938 } else {
939 /* action */
940 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
941 if (elem->schema->nodetype == LYS_ACTION) {
942 rpc_act = elem->schema;
943 break;
944 }
945 LY_TREE_DFS_END(rpc->tree, next, elem);
946 }
947 if (!rpc_act) {
948 ERRINT;
949 return NC_PSPOLL_ERROR;
950 }
951 }
952
953 if (!rpc_act->priv) {
954 /* no callback, reply with a not-implemented error */
Michal Vasko1a38c862016-01-15 15:50:07 +0100955 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +0100956 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +0200957 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +0100958 reply = clb(rpc->tree, session);
959 }
960
961 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100962 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +0100963 }
Michal Vasko71090fc2016-05-24 16:37:28 +0200964 r = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
965 if (reply->type == NC_RPL_ERROR) {
966 ret |= NC_PSPOLL_REPLY_ERROR;
967 }
968 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +0100969
Michal Vasko71090fc2016-05-24 16:37:28 +0200970 if (r == -1) {
971 ERR("Session %u: failed to write reply.", session->id);
972 ret |= NC_PSPOLL_ERROR;
973 }
Michal Vasko428087d2016-01-14 16:04:28 +0100974
975 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
976 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
977 session->status = NC_STATUS_INVALID;
978 }
979
Michal Vasko71090fc2016-05-24 16:37:28 +0200980 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100981}
982
983API int
Michal Vasko71090fc2016-05-24 16:37:28 +0200984nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
Michal Vasko428087d2016-01-14 16:04:28 +0100985{
986 int ret;
Michal Vaskobdcf2362016-07-26 11:35:43 +0200987 uint8_t q_id;
Michal Vasko3512e402016-01-28 16:22:34 +0100988 uint16_t i;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100989 time_t cur_time;
Michal Vasko71090fc2016-05-24 16:37:28 +0200990 struct nc_session *cur_session;
Michal Vasko4a827e52016-03-03 10:59:00 +0100991 struct nc_server_rpc *rpc = NULL;
Michal Vasko428087d2016-01-14 16:04:28 +0100992
993 if (!ps || !ps->session_count) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200994 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +0200995 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100996 }
997
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100998 cur_time = time(NULL);
999
Michal Vasko48a63ed2016-03-01 09:48:21 +01001000 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001001 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001002 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001003 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001004
Michal Vasko428087d2016-01-14 16:04:28 +01001005 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +01001006 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1007 ERR("Session %u: session not running.", ps->sessions[i]->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001008 ret = NC_PSPOLL_ERROR;
1009 if (session) {
1010 *session = ps->sessions[i];
1011 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001012 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001013 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001014
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001015 /* TODO invalidate only sessions without subscription */
Michal Vasko3a715132016-01-21 15:40:31 +01001016 if (server_opts.idle_timeout && (ps->sessions[i]->last_rpc + server_opts.idle_timeout >= cur_time)) {
1017 ERR("Session %u: session idle timeout elapsed.", ps->sessions[i]->id);
1018 ps->sessions[i]->status = NC_STATUS_INVALID;
1019 ps->sessions[i]->term_reason = NC_SESSION_TERM_TIMEOUT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001020 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1021 if (session) {
1022 *session = ps->sessions[i];
1023 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001024 goto finish;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001025 }
1026
Michal Vasko3a715132016-01-21 15:40:31 +01001027 if (ps->pfds[i].revents) {
Michal Vaskobd8ef262016-01-20 11:09:27 +01001028 break;
1029 }
Michal Vasko428087d2016-01-14 16:04:28 +01001030 }
1031
Michal Vaskobd8ef262016-01-20 11:09:27 +01001032 if (i == ps->session_count) {
Radek Krejci53691be2016-02-22 13:58:37 +01001033#ifdef NC_ENABLED_SSH
Michal Vasko3a715132016-01-21 15:40:31 +01001034retry_poll:
Michal Vasko3512e402016-01-28 16:22:34 +01001035#endif
Michal Vaskobd8ef262016-01-20 11:09:27 +01001036 /* no leftover event */
1037 i = 0;
Michal Vasko3a715132016-01-21 15:40:31 +01001038 ret = poll(ps->pfds, ps->session_count, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001039 if (ret < 0) {
1040 ERR("Poll failed (%s).", strerror(errno));
1041 ret = NC_PSPOLL_ERROR;
1042 goto finish;
1043 } else if (!ret) {
1044 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001045 goto finish;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001046 }
Michal Vasko428087d2016-01-14 16:04:28 +01001047 }
1048
Michal Vaskobd8ef262016-01-20 11:09:27 +01001049 /* find the first fd with POLLIN, we don't care if there are more now */
1050 for (; i < ps->session_count; ++i) {
Michal Vasko46eac552016-05-30 15:27:25 +02001051 if (ps->pfds[i].revents & (POLLHUP | POLLNVAL)) {
Michal Vasko3a715132016-01-21 15:40:31 +01001052 ERR("Session %u: communication socket unexpectedly closed.", ps->sessions[i]->id);
1053 ps->sessions[i]->status = NC_STATUS_INVALID;
1054 ps->sessions[i]->term_reason = NC_SESSION_TERM_DROPPED;
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 & POLLERR) {
1061 ERR("Session %u: communication socket error.", ps->sessions[i]->id);
1062 ps->sessions[i]->status = NC_STATUS_INVALID;
1063 ps->sessions[i]->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko71090fc2016-05-24 16:37:28 +02001064 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1065 if (session) {
1066 *session = ps->sessions[i];
1067 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001068 goto finish;
Michal Vasko3a715132016-01-21 15:40:31 +01001069 } else if (ps->pfds[i].revents & POLLIN) {
Radek Krejci53691be2016-02-22 13:58:37 +01001070#ifdef NC_ENABLED_SSH
Michal Vasko96164bf2016-01-21 15:41:58 +01001071 if (ps->sessions[i]->ti_type == NC_TI_LIBSSH) {
Michal Vasko3512e402016-01-28 16:22:34 +01001072 uint16_t j;
1073
Michal Vasko96164bf2016-01-21 15:41:58 +01001074 /* things are not that simple with SSH... */
Michal Vasko62be1ce2016-03-03 13:24:52 +01001075 ret = nc_ssh_pollin(ps->sessions[i], timeout);
Michal Vasko96164bf2016-01-21 15:41:58 +01001076
1077 /* clear POLLIN on sessions sharing this session's SSH session */
Michal Vasko71090fc2016-05-24 16:37:28 +02001078 if (ret & (NC_PSPOLL_RPC | NC_PSPOLL_SSH_MSG | NC_PSPOLL_SSH_CHANNEL)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001079 for (j = i + 1; j < ps->session_count; ++j) {
1080 if (ps->pfds[j].fd == ps->pfds[i].fd) {
1081 ps->pfds[j].revents = 0;
1082 }
1083 }
1084 }
1085
Michal Vasko71090fc2016-05-24 16:37:28 +02001086 /* SSH message only */
1087 if (!(ret & (NC_PSPOLL_RPC | NC_PSPOLL_PENDING))) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001088 ps->pfds[i].revents = 0;
Michal Vasko71090fc2016-05-24 16:37:28 +02001089 if (session) {
1090 *session = ps->sessions[i];
1091 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001092 goto finish;
Michal Vasko96164bf2016-01-21 15:41:58 +01001093
1094 /* event occurred on some other channel */
Michal Vasko71090fc2016-05-24 16:37:28 +02001095 } else if (ret & NC_PSPOLL_PENDING) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001096 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001097 if (i == ps->session_count - 1) {
1098 /* last session and it is not the right channel, ... */
Michal Vasko8c748832016-02-03 15:32:16 +01001099 if (!timeout) {
Michal Vasko428087d2016-01-14 16:04:28 +01001100 /* ... timeout is 0, so that is it */
Michal Vasko71090fc2016-05-24 16:37:28 +02001101 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001102 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001103 }
Michal Vasko8c748832016-02-03 15:32:16 +01001104 /* ... retry polling reasonable time apart ... */
1105 usleep(NC_TIMEOUT_STEP);
1106 if (timeout > 0) {
1107 /* ... and decrease timeout, if not -1 */
Michal Vasko7b38e232016-02-26 15:01:07 +01001108 timeout -= NC_TIMEOUT_STEP * 1000;
Michal Vasko8c748832016-02-03 15:32:16 +01001109 }
1110 goto retry_poll;
Michal Vasko428087d2016-01-14 16:04:28 +01001111 }
1112 /* check other sessions */
1113 continue;
Michal Vasko428087d2016-01-14 16:04:28 +01001114 }
1115 }
Radek Krejci53691be2016-02-22 13:58:37 +01001116#endif /* NC_ENABLED_SSH */
Michal Vasko428087d2016-01-14 16:04:28 +01001117
Michal Vaskobd8ef262016-01-20 11:09:27 +01001118 /* we are going to process it now */
Michal Vasko3a715132016-01-21 15:40:31 +01001119 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001120 break;
1121 }
1122 }
1123
1124 if (i == ps->session_count) {
1125 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001126 ret = NC_PSPOLL_ERROR;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001127 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001128 }
1129
1130 /* this is the session with some data available for reading */
Michal Vasko71090fc2016-05-24 16:37:28 +02001131 cur_session = ps->sessions[i];
1132 if (session) {
1133 *session = cur_session;
1134 }
Michal Vasko428087d2016-01-14 16:04:28 +01001135
Michal Vaskobd8ef262016-01-20 11:09:27 +01001136 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal Vasko71090fc2016-05-24 16:37:28 +02001137 ret = nc_timedlock(cur_session->ti_lock, timeout);
1138 if (ret < 0) {
1139 ret = NC_PSPOLL_ERROR;
1140 goto finish;
1141 } else if (!ret) {
1142 ret = NC_PSPOLL_TIMEOUT;
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 ret = nc_recv_rpc(cur_session, &rpc);
1147 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1148 pthread_mutex_unlock(cur_session->ti_lock);
1149 if (cur_session->status != NC_STATUS_RUNNING) {
1150 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001151 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001152 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001153 }
1154
Michal Vasko71090fc2016-05-24 16:37:28 +02001155 cur_session->last_rpc = time(NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001156
Michal Vasko428087d2016-01-14 16:04:28 +01001157 /* process RPC */
Michal Vasko71090fc2016-05-24 16:37:28 +02001158 ret |= nc_send_reply(cur_session, rpc);
Michal Vasko428087d2016-01-14 16:04:28 +01001159
Michal Vasko71090fc2016-05-24 16:37:28 +02001160 pthread_mutex_unlock(cur_session->ti_lock);
1161 if (cur_session->status != NC_STATUS_RUNNING) {
1162 ret |= NC_PSPOLL_SESSION_TERM;
1163 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1164 ret |= NC_PSPOLL_SESSION_ERROR;
1165 }
Michal Vasko428087d2016-01-14 16:04:28 +01001166 }
Radek Krejcif93c7d42016-04-06 13:41:15 +02001167
Michal Vaskoca4a2422016-02-02 12:17:14 +01001168 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vaskobd8ef262016-01-20 11:09:27 +01001169
1170 /* is there some other socket waiting? */
1171 for (++i; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +01001172 if (ps->pfds[i].revents) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001173 ret |= NC_PSPOLL_PENDING;
1174 break;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001175 }
1176 }
1177
Michal Vasko48a63ed2016-03-01 09:48:21 +01001178finish:
1179 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001180 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001181 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001182}
1183
Michal Vaskod09eae62016-02-01 10:32:52 +01001184API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001185nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001186{
Michal Vaskobdcf2362016-07-26 11:35:43 +02001187 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001188 uint16_t i;
1189 struct nc_session *session;
1190
Michal Vasko9a25e932016-02-01 10:36:42 +01001191 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001192 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001193 return;
1194 }
1195
Michal Vasko48a63ed2016-03-01 09:48:21 +01001196 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001197 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001198 return;
1199 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001200
Michal Vasko48a63ed2016-03-01 09:48:21 +01001201 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001202 for (i = 0; i < ps->session_count; i++) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001203 nc_session_free(ps->sessions[i], data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001204 }
1205 free(ps->sessions);
1206 ps->sessions = NULL;
1207 free(ps->pfds);
1208 ps->pfds = NULL;
1209 ps->session_count = 0;
1210 } else {
1211 for (i = 0; i < ps->session_count; ) {
1212 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1213 session = ps->sessions[i];
Radek Krejcid5f978f2016-03-03 13:14:45 +01001214 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001215 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001216 continue;
1217 }
1218
1219 ++i;
1220 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001221 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001222
1223 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001224 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001225}
1226
Radek Krejci53691be2016-02-22 13:58:37 +01001227#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001228
Michal Vasko3031aae2016-01-27 16:07:18 +01001229int
1230nc_server_add_endpt_listen(const char *name, const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001231{
1232 int sock;
Michal Vasko3031aae2016-01-27 16:07:18 +01001233 uint16_t i;
Radek Krejci53691be2016-02-22 13:58:37 +01001234#ifdef NC_ENABLED_SSH
Michal Vasko08a629a2016-02-02 12:20:47 +01001235 struct nc_server_ssh_opts *ssh_opts;
1236#endif
Michal Vasko9e036d52016-01-08 10:49:26 +01001237
Michal Vasko45e53ae2016-04-07 11:46:03 +02001238 if (!name) {
1239 ERRARG("name");
1240 return -1;
1241 } else if (!address) {
1242 ERRARG("address");
1243 return -1;
1244 } else if (!port) {
1245 ERRARG("port");
Michal Vasko9e036d52016-01-08 10:49:26 +01001246 return -1;
1247 }
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 Vaskod4c03a82016-02-08 15:27:26 +01001254 if ((server_opts.binds[i].ti == ti) && !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 Vasko9e036d52016-01-08 10:49:26 +01001262 sock = nc_sock_listen(address, port);
1263 if (sock == -1) {
Michal Vasko51e514d2016-02-02 15:51:52 +01001264 /* WRITE UNLOCK */
1265 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001266 return -1;
1267 }
1268
Michal Vasko3031aae2016-01-27 16:07:18 +01001269 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001270 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
1271 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
1272 if (!server_opts.binds || !server_opts.endpts) {
1273 ERRMEM;
1274 /* WRITE UNLOCK */
1275 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko0f74da52016-03-03 08:52:52 +01001276 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001277 return -1;
1278 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001279
Michal Vasko3031aae2016-01-27 16:07:18 +01001280 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
1281 server_opts.binds[server_opts.endpt_count - 1].address = lydict_insert(server_opts.ctx, address, 0);
Michal Vasko3031aae2016-01-27 16:07:18 +01001282 server_opts.binds[server_opts.endpt_count - 1].port = port;
1283 server_opts.binds[server_opts.endpt_count - 1].sock = sock;
1284 server_opts.binds[server_opts.endpt_count - 1].ti = ti;
1285 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001286#ifdef NC_ENABLED_SSH
Michal Vasko3031aae2016-01-27 16:07:18 +01001287 case NC_TI_LIBSSH:
Michal Vasko08a629a2016-02-02 12:20:47 +01001288 ssh_opts = calloc(1, sizeof *ssh_opts);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001289 if (!ssh_opts) {
1290 ERRMEM;
1291 /* WRITE UNLOCK */
1292 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1293 return -1;
1294 }
Michal Vasko08a629a2016-02-02 12:20:47 +01001295 /* set default values */
1296 ssh_opts->auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1297 ssh_opts->auth_attempts = 3;
1298 ssh_opts->auth_timeout = 10;
1299
1300 server_opts.endpts[server_opts.endpt_count - 1].ti_opts = ssh_opts;
Michal Vasko3031aae2016-01-27 16:07:18 +01001301 break;
1302#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001303#ifdef NC_ENABLED_TLS
Michal Vasko3031aae2016-01-27 16:07:18 +01001304 case NC_TI_OPENSSL:
1305 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 +01001306 if (!server_opts.endpts[server_opts.endpt_count - 1].ti_opts) {
1307 ERRMEM;
1308 /* WRITE UNLOCK */
1309 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1310 return -1;
1311 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001312 break;
1313#endif
1314 default:
1315 ERRINT;
1316 server_opts.endpts[server_opts.endpt_count - 1].ti_opts = NULL;
1317 break;
1318 }
1319 pthread_mutex_init(&server_opts.endpts[server_opts.endpt_count - 1].endpt_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001320
Michal Vasko3031aae2016-01-27 16:07:18 +01001321 /* WRITE UNLOCK */
1322 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001323
Michal Vasko9e036d52016-01-08 10:49:26 +01001324 return 0;
1325}
1326
Michal Vasko3031aae2016-01-27 16:07:18 +01001327int
Michal Vaskoda514772016-02-01 11:32:01 +01001328nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1329{
1330 struct nc_endpt *endpt;
1331 struct nc_bind *bind = NULL;
1332 uint16_t i;
1333 int sock;
1334
Michal Vasko45e53ae2016-04-07 11:46:03 +02001335 if (!endpt_name) {
1336 ERRARG("endpt_name");
1337 return -1;
1338 } else if ((!address && !port) || (address && port)) {
1339 ERRARG("address and port");
1340 return -1;
1341 } else if (!ti) {
1342 ERRARG("ti");
Michal Vaskoda514772016-02-01 11:32:01 +01001343 return -1;
1344 }
1345
Michal Vasko51e514d2016-02-02 15:51:52 +01001346 /* LOCK */
Michal Vaskoda514772016-02-01 11:32:01 +01001347 endpt = nc_server_endpt_lock(endpt_name, ti);
1348 if (!endpt) {
1349 return -1;
1350 }
1351
1352 /* we need to learn the index, to get the bind :-/ */
1353 for (i = 0; i < server_opts.endpt_count; ++i) {
1354 if (&server_opts.endpts[i] == endpt) {
1355 bind = &server_opts.binds[i];
1356 }
1357 }
1358 if (!bind) {
1359 ERRINT;
Michal Vasko51e514d2016-02-02 15:51:52 +01001360 goto fail;
Michal Vaskoda514772016-02-01 11:32:01 +01001361 }
1362
1363 if (address) {
1364 sock = nc_sock_listen(address, bind->port);
1365 } else {
1366 sock = nc_sock_listen(bind->address, port);
1367 }
1368 if (sock == -1) {
Michal Vasko51e514d2016-02-02 15:51:52 +01001369 goto fail;
Michal Vaskoda514772016-02-01 11:32:01 +01001370 }
1371
1372 /* close old socket, update parameters */
1373 close(bind->sock);
1374 bind->sock = sock;
1375 if (address) {
1376 lydict_remove(server_opts.ctx, bind->address);
1377 bind->address = lydict_insert(server_opts.ctx, address, 0);
1378 } else {
1379 bind->port = port;
1380 }
1381
Michal Vasko51e514d2016-02-02 15:51:52 +01001382 /* UNLOCK */
Michal Vasko7a93af72016-02-01 16:00:15 +01001383 nc_server_endpt_unlock(endpt);
Michal Vaskoda514772016-02-01 11:32:01 +01001384 return 0;
Michal Vasko51e514d2016-02-02 15:51:52 +01001385
1386fail:
1387 /* UNLOCK */
1388 nc_server_endpt_unlock(endpt);
1389 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001390}
1391
1392int
Michal Vasko3031aae2016-01-27 16:07:18 +01001393nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001394{
1395 uint32_t i;
1396 int ret = -1;
1397
Michal Vasko3031aae2016-01-27 16:07:18 +01001398 /* WRITE LOCK */
1399 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001400
Michal Vasko3031aae2016-01-27 16:07:18 +01001401 if (!name && !ti) {
1402 /* remove all */
Michal Vasko3031aae2016-01-27 16:07:18 +01001403 for (i = 0; i < server_opts.endpt_count; ++i) {
1404 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko11d142a2016-01-19 15:58:24 +01001405 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
Michal Vasko51e514d2016-02-02 15:51:52 +01001406
Michal Vasko3031aae2016-01-27 16:07:18 +01001407 close(server_opts.binds[i].sock);
1408 pthread_mutex_destroy(&server_opts.endpts[i].endpt_lock);
1409 switch (server_opts.binds[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001410#ifdef NC_ENABLED_SSH
Michal Vasko3031aae2016-01-27 16:07:18 +01001411 case NC_TI_LIBSSH:
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001412 nc_server_ssh_clear_opts(server_opts.endpts[i].ti_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001413 break;
1414#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001415#ifdef NC_ENABLED_TLS
Michal Vasko3031aae2016-01-27 16:07:18 +01001416 case NC_TI_OPENSSL:
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001417 nc_server_tls_clear_opts(server_opts.endpts[i].ti_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001418 break;
1419#endif
1420 default:
1421 ERRINT;
1422 break;
1423 }
1424 free(server_opts.endpts[i].ti_opts);
Michal Vasko9e036d52016-01-08 10:49:26 +01001425
Michal Vasko9e036d52016-01-08 10:49:26 +01001426 ret = 0;
1427 }
Michal Vasko7ddc5702016-02-08 15:29:39 +01001428 free(server_opts.binds);
1429 server_opts.binds = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +01001430 free(server_opts.endpts);
1431 server_opts.endpts = NULL;
1432 server_opts.endpt_count = 0;
1433
Michal Vasko1a38c862016-01-15 15:50:07 +01001434 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +01001435 /* remove one name endpoint or all ti endpoints */
1436 for (i = 0; i < server_opts.endpt_count; ++i) {
1437 if ((server_opts.binds[i].ti == ti) &&
1438 (!name || !strcmp(server_opts.endpts[i].name, name))) {
1439
Michal Vasko3031aae2016-01-27 16:07:18 +01001440 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko11d142a2016-01-19 15:58:24 +01001441 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
Michal Vasko3031aae2016-01-27 16:07:18 +01001442 close(server_opts.binds[i].sock);
1443 pthread_mutex_destroy(&server_opts.endpts[i].endpt_lock);
1444 switch (server_opts.binds[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001445#ifdef NC_ENABLED_SSH
Michal Vasko3031aae2016-01-27 16:07:18 +01001446 case NC_TI_LIBSSH:
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001447 nc_server_ssh_clear_opts(server_opts.endpts[i].ti_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001448 break;
1449#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001450#ifdef NC_ENABLED_TLS
Michal Vasko3031aae2016-01-27 16:07:18 +01001451 case NC_TI_OPENSSL:
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001452 nc_server_tls_clear_opts(server_opts.endpts[i].ti_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001453 break;
1454#endif
1455 default:
1456 ERRINT;
1457 break;
1458 }
1459 free(server_opts.endpts[i].ti_opts);
Michal Vasko1a38c862016-01-15 15:50:07 +01001460
Michal Vasko3031aae2016-01-27 16:07:18 +01001461 --server_opts.endpt_count;
Michal Vaskoc0256492016-02-02 12:19:06 +01001462 if (i < server_opts.endpt_count) {
1463 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1464 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
1465 } else if (!server_opts.endpt_count) {
1466 free(server_opts.binds);
1467 server_opts.binds = NULL;
1468 free(server_opts.endpts);
1469 server_opts.endpts = NULL;
1470 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001471
1472 ret = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001473
1474 if (name) {
1475 /* one name endpoint removed, they are unique, we're done */
1476 break;
1477 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001478 }
1479 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001480 }
1481
Michal Vasko3031aae2016-01-27 16:07:18 +01001482 /* WRITE UNLOCK */
1483 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001484
Michal Vasko9e036d52016-01-08 10:49:26 +01001485 return ret;
1486}
1487
Michal Vasko71090fc2016-05-24 16:37:28 +02001488API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01001489nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001490{
Michal Vasko71090fc2016-05-24 16:37:28 +02001491 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01001492 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01001493 char *host = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +01001494 uint16_t port, idx;
Michal Vasko9e036d52016-01-08 10:49:26 +01001495
Michal Vasko45e53ae2016-04-07 11:46:03 +02001496 if (!server_opts.ctx) {
1497 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001498 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001499 } else if (!session) {
1500 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001501 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001502 }
1503
Michal Vasko51e514d2016-02-02 15:51:52 +01001504 /* we have to hold WRITE for the whole time, since there is not
1505 * a way of downgrading the lock to READ */
1506 /* WRITE LOCK */
1507 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
1508
1509 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02001510 ERR("No endpoints to accept sessions on.");
Michal Vasko51e514d2016-02-02 15:51:52 +01001511 /* WRITE UNLOCK */
1512 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001513 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01001514 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001515
Michal Vasko3031aae2016-01-27 16:07:18 +01001516 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &idx);
Michal Vaskob48aa812016-01-18 14:13:09 +01001517
Michal Vasko50456e82016-02-02 12:16:08 +01001518 if (ret < 1) {
Michal Vasko51e514d2016-02-02 15:51:52 +01001519 /* WRITE UNLOCK */
Michal Vasko3031aae2016-01-27 16:07:18 +01001520 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01001521 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02001522 if (!ret) {
1523 return NC_MSG_WOULDBLOCK;
1524 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001525 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001526 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001527 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001528
Michal Vasko1a38c862016-01-15 15:50:07 +01001529 *session = calloc(1, sizeof **session);
Michal Vasko686aa312016-01-21 15:58:18 +01001530 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001531 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001532 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01001533 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02001534 msgtype = NC_MSG_ERROR;
1535 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001536 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001537 (*session)->status = NC_STATUS_STARTING;
1538 (*session)->side = NC_SERVER;
1539 (*session)->ctx = server_opts.ctx;
1540 (*session)->flags = NC_SESSION_SHAREDCTX;
1541 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
1542 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01001543
1544 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +01001545 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1546 if (!(*session)->ti_lock) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001547 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001548 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001549 msgtype = NC_MSG_ERROR;
1550 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001551 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001552 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001553
Michal Vasko2cc4c682016-03-01 09:16:48 +01001554 (*session)->data = server_opts.endpts[idx].ti_opts;
Michal Vasko3031aae2016-01-27 16:07:18 +01001555
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001556 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001557#ifdef NC_ENABLED_SSH
Michal Vasko3031aae2016-01-27 16:07:18 +01001558 if (server_opts.binds[idx].ti == NC_TI_LIBSSH) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001559 ret = nc_accept_ssh_session(*session, sock, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001560 if (ret < 0) {
1561 msgtype = NC_MSG_ERROR;
1562 goto cleanup;
1563 } else if (!ret) {
1564 msgtype = NC_MSG_WOULDBLOCK;
1565 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001566 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001567 } else
1568#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001569#ifdef NC_ENABLED_TLS
Michal Vasko3d865d22016-01-28 16:00:53 +01001570 if (server_opts.binds[idx].ti == NC_TI_OPENSSL) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001571 ret = nc_accept_tls_session(*session, sock, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001572 if (ret < 0) {
1573 msgtype = NC_MSG_ERROR;
1574 goto cleanup;
1575 } else if (!ret) {
1576 msgtype = NC_MSG_WOULDBLOCK;
1577 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001578 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001579 } else
1580#endif
1581 {
Michal Vasko9e036d52016-01-08 10:49:26 +01001582 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001583 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001584 msgtype = NC_MSG_ERROR;
1585 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001586 }
1587
Michal Vasko2cc4c682016-03-01 09:16:48 +01001588 (*session)->data = NULL;
1589
Michal Vasko51e514d2016-02-02 15:51:52 +01001590 /* WRITE UNLOCK */
Michal Vasko3031aae2016-01-27 16:07:18 +01001591 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1592
Michal Vaskob48aa812016-01-18 14:13:09 +01001593 /* assign new SID atomically */
1594 /* LOCK */
1595 pthread_spin_lock(&server_opts.sid_lock);
1596 (*session)->id = server_opts.new_session_id++;
1597 /* UNLOCK */
1598 pthread_spin_unlock(&server_opts.sid_lock);
1599
Michal Vasko9e036d52016-01-08 10:49:26 +01001600 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001601 msgtype = nc_handshake(*session);
1602 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001603 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001604 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001605 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001606 }
Michal Vaskof8352352016-05-24 09:11:36 +02001607 (*session)->session_start = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001608 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01001609
Michal Vasko71090fc2016-05-24 16:37:28 +02001610 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001611
Michal Vasko71090fc2016-05-24 16:37:28 +02001612cleanup:
Michal Vasko3031aae2016-01-27 16:07:18 +01001613 /* WRITE UNLOCK */
1614 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1615
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001616 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001617 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001618 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001619}
1620
Michal Vasko71090fc2016-05-24 16:37:28 +02001621NC_MSG_TYPE
Michal Vasko8f5270d2016-02-29 16:22:25 +01001622nc_connect_callhome(const char *host, uint16_t port, NC_TRANSPORT_IMPL ti, struct nc_session **session)
Michal Vaskob05053d2016-01-22 16:12:06 +01001623{
Michal Vasko71090fc2016-05-24 16:37:28 +02001624 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01001625 int sock, ret;
1626
Michal Vasko45e53ae2016-04-07 11:46:03 +02001627 if (!host) {
1628 ERRARG("host");
Michal Vasko71090fc2016-05-24 16:37:28 +02001629 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001630 } else if (!port) {
1631 ERRARG("port");
Michal Vasko71090fc2016-05-24 16:37:28 +02001632 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001633 } else if (!ti) {
1634 ERRARG("ti");
Michal Vasko71090fc2016-05-24 16:37:28 +02001635 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001636 } else if (!session) {
1637 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001638 return NC_MSG_ERROR;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001639 }
1640
Michal Vaskob05053d2016-01-22 16:12:06 +01001641 sock = nc_sock_connect(host, port);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001642 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001643 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01001644 }
1645
1646 *session = calloc(1, sizeof **session);
1647 if (!(*session)) {
1648 ERRMEM;
1649 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001650 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01001651 }
1652 (*session)->status = NC_STATUS_STARTING;
1653 (*session)->side = NC_SERVER;
1654 (*session)->ctx = server_opts.ctx;
1655 (*session)->flags = NC_SESSION_SHAREDCTX | NC_SESSION_CALLHOME;
Michal Vaskob05053d2016-01-22 16:12:06 +01001656 (*session)->host = lydict_insert(server_opts.ctx, host, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01001657 (*session)->port = port;
1658
1659 /* transport lock */
1660 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1661 if (!(*session)->ti_lock) {
1662 ERRMEM;
1663 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001664 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01001665 goto fail;
1666 }
1667 pthread_mutex_init((*session)->ti_lock, NULL);
1668
1669 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001670#ifdef NC_ENABLED_SSH
Michal Vaskob05053d2016-01-22 16:12:06 +01001671 if (ti == NC_TI_LIBSSH) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001672 /* OPTS LOCK */
1673 pthread_mutex_lock(&ssh_ch_opts_lock);
1674
Michal Vasko2cc4c682016-03-01 09:16:48 +01001675 (*session)->data = &ssh_ch_opts;
Michal Vasko0190bc32016-03-02 15:47:49 +01001676 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01001677 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001678
1679 /* OPTS UNLOCK */
1680 pthread_mutex_unlock(&ssh_ch_opts_lock);
1681
Michal Vasko71090fc2016-05-24 16:37:28 +02001682 if (ret < 0) {
1683 msgtype = NC_MSG_ERROR;
1684 goto fail;
1685 } else if (!ret) {
1686 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01001687 goto fail;
1688 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001689 } else
1690#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001691#ifdef NC_ENABLED_TLS
Michal Vasko3d865d22016-01-28 16:00:53 +01001692 if (ti == NC_TI_OPENSSL) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001693 /* OPTS LOCK */
1694 pthread_mutex_lock(&tls_ch_opts_lock);
1695
Michal Vasko2cc4c682016-03-01 09:16:48 +01001696 (*session)->data = &tls_ch_opts;
Michal Vasko0190bc32016-03-02 15:47:49 +01001697 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01001698 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001699
1700 /* OPTS UNLOCK */
1701 pthread_mutex_unlock(&tls_ch_opts_lock);
1702
Michal Vasko71090fc2016-05-24 16:37:28 +02001703 if (ret < 0) {
1704 msgtype = NC_MSG_ERROR;
1705 goto fail;
1706 } else if (!ret) {
1707 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01001708 goto fail;
1709 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001710 } else
1711#endif
1712 {
Michal Vaskob05053d2016-01-22 16:12:06 +01001713 ERRINT;
1714 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001715 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01001716 goto fail;
1717 }
1718
1719 /* assign new SID atomically */
1720 /* LOCK */
1721 pthread_spin_lock(&server_opts.sid_lock);
1722 (*session)->id = server_opts.new_session_id++;
1723 /* UNLOCK */
1724 pthread_spin_unlock(&server_opts.sid_lock);
1725
1726 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001727 msgtype = nc_handshake(*session);
1728 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01001729 goto fail;
1730 }
Michal Vaskof8352352016-05-24 09:11:36 +02001731 (*session)->session_start = time(NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01001732 (*session)->status = NC_STATUS_RUNNING;
1733
Michal Vasko71090fc2016-05-24 16:37:28 +02001734 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01001735
1736fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001737 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01001738 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001739 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01001740}
1741
Radek Krejci53691be2016-02-22 13:58:37 +01001742#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02001743
Michal Vaskoc45ebd32016-05-25 11:17:36 +02001744API time_t
1745nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02001746{
1747 if (!session) {
1748 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02001749 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02001750 }
1751
Michal Vaskoc45ebd32016-05-25 11:17:36 +02001752 return session->session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02001753}