blob: 25b888c1065fc51ab3b6b7db8517eeb375b4a052 [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{
84 if (!session || !reason) {
85 ERRARG;
86 return;
87 }
88
89 session->term_reason = reason;
90}
91
Michal Vasko086311b2016-01-08 09:53:11 +010092int
Michal Vaskof05562c2016-01-20 12:06:43 +010093nc_sock_listen(const char *address, uint16_t port)
Michal Vasko086311b2016-01-08 09:53:11 +010094{
95 const int optVal = 1;
96 const socklen_t optLen = sizeof(optVal);
97 int is_ipv4, sock;
98 struct sockaddr_storage saddr;
99
100 struct sockaddr_in *saddr4;
101 struct sockaddr_in6 *saddr6;
102
103
104 if (!strchr(address, ':')) {
105 is_ipv4 = 1;
106 } else {
107 is_ipv4 = 0;
108 }
109
110 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
111 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100112 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100113 goto fail;
114 }
115
116 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&optVal, optLen)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100117 ERR("Could not set socket SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100118 goto fail;
119 }
120
121 bzero(&saddr, sizeof(struct sockaddr_storage));
122 if (is_ipv4) {
123 saddr4 = (struct sockaddr_in *)&saddr;
124
125 saddr4->sin_family = AF_INET;
126 saddr4->sin_port = htons(port);
127
128 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100129 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100130 goto fail;
131 }
132
133 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100134 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100135 goto fail;
136 }
137
138 } else {
139 saddr6 = (struct sockaddr_in6 *)&saddr;
140
141 saddr6->sin6_family = AF_INET6;
142 saddr6->sin6_port = htons(port);
143
144 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100145 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100146 goto fail;
147 }
148
149 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100150 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100151 goto fail;
152 }
153 }
154
Michal Vaskofb89d772016-01-08 12:25:35 +0100155 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100156 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100157 goto fail;
158 }
159
160 return sock;
161
162fail:
163 if (sock > -1) {
164 close(sock);
165 }
166
167 return -1;
168}
169
170int
Michal Vasko3031aae2016-01-27 16:07:18 +0100171nc_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 +0100172{
173 uint16_t i;
174 struct pollfd *pfd;
175 struct sockaddr_storage saddr;
176 socklen_t saddr_len = sizeof(saddr);
Michal Vasko0190bc32016-03-02 15:47:49 +0100177 int ret, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100178
179 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100180 if (!pfd) {
181 ERRMEM;
182 return -1;
183 }
184
Michal Vasko086311b2016-01-08 09:53:11 +0100185 for (i = 0; i < bind_count; ++i) {
186 pfd[i].fd = binds[i].sock;
187 pfd[i].events = POLLIN;
188 pfd[i].revents = 0;
189 }
190
191 /* poll for a new connection */
192 errno = 0;
193 ret = poll(pfd, bind_count, timeout);
194 if (!ret) {
195 /* we timeouted */
196 free(pfd);
197 return 0;
198 } else if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100199 ERR("Poll failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100200 free(pfd);
201 return -1;
202 }
203
204 for (i = 0; i < bind_count; ++i) {
205 if (pfd[i].revents & POLLIN) {
206 sock = pfd[i].fd;
207 break;
208 }
209 }
210 free(pfd);
211
212 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100213 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100214 return -1;
215 }
216
217 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100218 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100219 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100220 return -1;
221 }
222
Michal Vasko0190bc32016-03-02 15:47:49 +0100223 /* make the socket non-blocking */
224 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
225 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100226 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100227 return -1;
228 }
229
Michal Vasko3031aae2016-01-27 16:07:18 +0100230 if (idx) {
231 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100232 }
233
Michal Vasko086311b2016-01-08 09:53:11 +0100234 /* host was requested */
235 if (host) {
236 if (saddr.ss_family == AF_INET) {
237 *host = malloc(15);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100238 if (*host) {
239 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, 15)) {
240 ERR("inet_ntop failed (%s).", strerror(errno));
241 free(*host);
242 *host = NULL;
243 }
Michal Vasko086311b2016-01-08 09:53:11 +0100244
Michal Vasko4eb3c312016-03-01 14:09:37 +0100245 if (port) {
246 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
247 }
248 } else {
249 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100250 }
251 } else if (saddr.ss_family == AF_INET6) {
252 *host = malloc(40);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100253 if (*host) {
254 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, 40)) {
255 ERR("inet_ntop failed (%s).", strerror(errno));
256 free(*host);
257 *host = NULL;
258 }
Michal Vasko086311b2016-01-08 09:53:11 +0100259
Michal Vasko4eb3c312016-03-01 14:09:37 +0100260 if (port) {
261 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
262 }
263 } else {
264 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100265 }
266 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100267 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100268 }
269 }
270
271 return ret;
272}
273
Michal Vasko05ba9df2016-01-13 14:40:27 +0100274static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100275nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100276{
277 const char *identifier = NULL, *version = NULL, *format = NULL;
278 char *model_data = NULL;
279 const struct lys_module *module;
280 struct nc_server_error *err;
281 struct lyd_node *child, *data = NULL;
Michal Vasko11d142a2016-01-19 15:58:24 +0100282 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100283
284 LY_TREE_FOR(rpc->child, child) {
285 if (!strcmp(child->schema->name, "identifier")) {
286 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
287 } else if (!strcmp(child->schema->name, "version")) {
288 version = ((struct lyd_node_leaf_list *)child)->value_str;
289 } else if (!strcmp(child->schema->name, "format")) {
290 format = ((struct lyd_node_leaf_list *)child)->value_str;
291 }
292 }
293
294 /* check version */
295 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100296 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
297 nc_err_set_msg(err, "The requested version is not supported.", "en");
298 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100299 }
300
301 /* check and get module with the name identifier */
302 module = ly_ctx_get_module(server_opts.ctx, identifier, version);
303 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200304 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
305 }
306 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100307 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
308 nc_err_set_msg(err, "The requested schema was not found.", "en");
309 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100310 }
311
312 /* check format */
313 if (!format || !strcmp(format, "yang")) {
314 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL);
315 } else if (!strcmp(format, "yin")) {
316 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL);
317 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100318 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
319 nc_err_set_msg(err, "The requested format is not supported.", "en");
320 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100321 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200322 if (!model_data) {
323 ERRINT;
324 return NULL;
325 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100326
Michal Vasko303245c2016-03-24 15:20:03 +0100327 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/output/data");
Michal Vaskod91f6e62016-04-05 11:34:22 +0200328 if (!sdata) {
329 ERRINT;
330 free(model_data);
331 return NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100332 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200333
334 data = lyd_output_new_anyxml_str(sdata, model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100335 if (!data) {
336 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200337 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100338 return NULL;
339 }
340
341 return nc_server_reply_data(data, NC_PARAMTYPE_FREE);
342}
343
344static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100345nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100346{
Michal Vasko428087d2016-01-14 16:04:28 +0100347 session->term_reason = NC_SESSION_TERM_CLOSED;
348 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100349}
350
Michal Vasko086311b2016-01-08 09:53:11 +0100351API int
352nc_server_init(struct ly_ctx *ctx)
353{
Michal Vasko05ba9df2016-01-13 14:40:27 +0100354 const struct lys_node *rpc;
355
Michal Vasko086311b2016-01-08 09:53:11 +0100356 if (!ctx) {
357 ERRARG;
358 return -1;
359 }
360
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100361 nc_init();
362
Michal Vasko05ba9df2016-01-13 14:40:27 +0100363 /* set default <get-schema> callback if not specified */
Michal Vasko303245c2016-03-24 15:20:03 +0100364 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema");
Michal Vaskofd100c92016-03-01 15:23:46 +0100365 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100366 lys_set_private(rpc, nc_clb_default_get_schema);
367 }
368
369 /* set default <close-session> callback if not specififed */
Michal Vasko303245c2016-03-24 15:20:03 +0100370 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session");
Michal Vaskofd100c92016-03-01 15:23:46 +0100371 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100372 lys_set_private(rpc, nc_clb_default_close_session);
373 }
374
Michal Vasko086311b2016-01-08 09:53:11 +0100375 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100376
377 server_opts.new_session_id = 1;
378 pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
379
Michal Vasko086311b2016-01-08 09:53:11 +0100380 return 0;
381}
382
Michal Vaskob48aa812016-01-18 14:13:09 +0100383API void
384nc_server_destroy(void)
385{
386 pthread_spin_destroy(&server_opts.sid_lock);
387
Radek Krejci53691be2016-02-22 13:58:37 +0100388#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko3031aae2016-01-27 16:07:18 +0100389 nc_server_del_endpt(NULL, 0);
Michal Vaskob48aa812016-01-18 14:13:09 +0100390#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100391 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100392}
393
Michal Vasko086311b2016-01-08 09:53:11 +0100394API int
395nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
396{
397 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)
398 || (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT)))) {
399 ERRARG;
400 return -1;
401 }
402
403 server_opts.wd_basic_mode = basic_mode;
404 server_opts.wd_also_supported = also_supported;
405 return 0;
406}
407
Michal Vasko1a38c862016-01-15 15:50:07 +0100408API void
Michal Vasko086311b2016-01-08 09:53:11 +0100409nc_server_set_capab_interleave(int interleave_support)
410{
411 if (interleave_support) {
412 server_opts.interleave_capab = 1;
413 } else {
414 server_opts.interleave_capab = 0;
415 }
Michal Vasko086311b2016-01-08 09:53:11 +0100416}
417
Michal Vasko1a38c862016-01-15 15:50:07 +0100418API void
Michal Vasko086311b2016-01-08 09:53:11 +0100419nc_server_set_hello_timeout(uint16_t hello_timeout)
420{
Michal Vasko086311b2016-01-08 09:53:11 +0100421 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100422}
423
Michal Vasko1a38c862016-01-15 15:50:07 +0100424API void
Michal Vasko086311b2016-01-08 09:53:11 +0100425nc_server_set_idle_timeout(uint16_t idle_timeout)
426{
Michal Vasko086311b2016-01-08 09:53:11 +0100427 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100428}
429
430API int
Michal Vasko1a38c862016-01-15 15:50:07 +0100431nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100432{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100433 if (!server_opts.ctx || (fdin < 0) || (fdout < 0) || !username || !session) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100434 ERRARG;
435 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100436 }
437
438 /* prepare session structure */
Michal Vasko1a38c862016-01-15 15:50:07 +0100439 *session = calloc(1, sizeof **session);
440 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100441 ERRMEM;
Michal Vasko1a38c862016-01-15 15:50:07 +0100442 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100443 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100444 (*session)->status = NC_STATUS_STARTING;
445 (*session)->side = NC_SERVER;
Michal Vasko086311b2016-01-08 09:53:11 +0100446
447 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100448 (*session)->ti_type = NC_TI_FD;
449 (*session)->ti.fd.in = fdin;
450 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100451
452 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100453 (*session)->flags = NC_SESSION_SHAREDCTX;
454 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100455
Michal Vaskob48aa812016-01-18 14:13:09 +0100456 /* assign new SID atomically */
457 pthread_spin_lock(&server_opts.sid_lock);
458 (*session)->id = server_opts.new_session_id++;
459 pthread_spin_unlock(&server_opts.sid_lock);
460
Michal Vasko086311b2016-01-08 09:53:11 +0100461 /* NETCONF handshake */
Michal Vasko1a38c862016-01-15 15:50:07 +0100462 if (nc_handshake(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100463 goto fail;
464 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100465 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100466 (*session)->last_rpc = time(NULL);
Michal Vasko086311b2016-01-08 09:53:11 +0100467
Michal Vasko1a38c862016-01-15 15:50:07 +0100468 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100469
470fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100471 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +0100472 *session = NULL;
473 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100474}
Michal Vasko9e036d52016-01-08 10:49:26 +0100475
Michal Vasko428087d2016-01-14 16:04:28 +0100476API struct nc_pollsession *
477nc_ps_new(void)
478{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100479 struct nc_pollsession *ps;
480
481 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100482 if (!ps) {
483 ERRMEM;
484 return NULL;
485 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100486 pthread_mutex_init(&ps->lock, NULL);
487
488 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100489}
490
491API void
492nc_ps_free(struct nc_pollsession *ps)
493{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100494 if (!ps) {
495 return;
496 }
497
Michal Vasko3a715132016-01-21 15:40:31 +0100498 free(ps->pfds);
Michal Vasko428087d2016-01-14 16:04:28 +0100499 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100500 pthread_mutex_destroy(&ps->lock);
501
Michal Vasko428087d2016-01-14 16:04:28 +0100502 free(ps);
503}
504
505API int
506nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
507{
508 if (!ps || !session) {
509 ERRARG;
510 return -1;
511 }
512
Michal Vasko48a63ed2016-03-01 09:48:21 +0100513 /* LOCK */
514 pthread_mutex_lock(&ps->lock);
515
Michal Vasko428087d2016-01-14 16:04:28 +0100516 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100517 ps->pfds = nc_realloc(ps->pfds, ps->session_count * sizeof *ps->pfds);
518 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
519 if (!ps->pfds || !ps->sessions) {
520 ERRMEM;
521 /* UNLOCK */
522 pthread_mutex_unlock(&ps->lock);
523 return -1;
524 }
Michal Vasko428087d2016-01-14 16:04:28 +0100525
526 switch (session->ti_type) {
527 case NC_TI_FD:
Michal Vasko3a715132016-01-21 15:40:31 +0100528 ps->pfds[ps->session_count - 1].fd = session->ti.fd.in;
Michal Vasko428087d2016-01-14 16:04:28 +0100529 break;
530
Radek Krejci53691be2016-02-22 13:58:37 +0100531#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100532 case NC_TI_LIBSSH:
Michal Vasko3a715132016-01-21 15:40:31 +0100533 ps->pfds[ps->session_count - 1].fd = ssh_get_fd(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100534 break;
535#endif
536
Radek Krejci53691be2016-02-22 13:58:37 +0100537#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100538 case NC_TI_OPENSSL:
Michal Vasko3a715132016-01-21 15:40:31 +0100539 ps->pfds[ps->session_count - 1].fd = SSL_get_rfd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100540 break;
541#endif
542
543 default:
544 ERRINT;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100545 /* UNLOCK */
546 pthread_mutex_unlock(&ps->lock);
Michal Vasko428087d2016-01-14 16:04:28 +0100547 return -1;
548 }
Michal Vasko3a715132016-01-21 15:40:31 +0100549 ps->pfds[ps->session_count - 1].events = POLLIN;
550 ps->pfds[ps->session_count - 1].revents = 0;
551 ps->sessions[ps->session_count - 1] = session;
Michal Vasko428087d2016-01-14 16:04:28 +0100552
Michal Vasko48a63ed2016-03-01 09:48:21 +0100553 /* UNLOCK */
554 pthread_mutex_unlock(&ps->lock);
555
Michal Vasko428087d2016-01-14 16:04:28 +0100556 return 0;
557}
558
Michal Vasko48a63ed2016-03-01 09:48:21 +0100559static int
Radek Krejcid5f978f2016-03-03 13:14:45 +0100560_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +0100561{
562 uint16_t i;
563
Radek Krejcid5f978f2016-03-03 13:14:45 +0100564 if (index >= 0) {
565 i = (uint16_t)index;
566 goto remove;
567 }
Michal Vasko428087d2016-01-14 16:04:28 +0100568 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100569 if (ps->sessions[i] == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +0100570remove:
Michal Vasko428087d2016-01-14 16:04:28 +0100571 --ps->session_count;
Michal Vasko58005732016-02-02 15:50:52 +0100572 if (i < ps->session_count) {
573 ps->sessions[i] = ps->sessions[ps->session_count];
574 memcpy(&ps->pfds[i], &ps->pfds[ps->session_count], sizeof *ps->pfds);
575 } else if (!ps->session_count) {
576 free(ps->sessions);
577 ps->sessions = NULL;
578 free(ps->pfds);
579 ps->pfds = NULL;
580 }
Michal Vasko428087d2016-01-14 16:04:28 +0100581 return 0;
582 }
583 }
584
Michal Vaskof0537d82016-01-29 14:42:38 +0100585 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +0100586}
587
Michal Vasko48a63ed2016-03-01 09:48:21 +0100588API int
589nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
590{
591 int ret;
592
593 if (!ps || !session) {
594 ERRARG;
595 return -1;
596 }
597
598 /* LOCK */
599 pthread_mutex_lock(&ps->lock);
600
Radek Krejcid5f978f2016-03-03 13:14:45 +0100601 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100602
603 /* UNLOCK */
604 pthread_mutex_unlock(&ps->lock);
605
606 return ret;
607}
608
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100609API uint16_t
610nc_ps_session_count(struct nc_pollsession *ps)
611{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100612 uint16_t count;
613
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100614 if (!ps) {
615 ERRARG;
616 return 0;
617 }
618
Michal Vasko48a63ed2016-03-01 09:48:21 +0100619 /* LOCK */
620 pthread_mutex_lock(&ps->lock);
621
622 count = ps->session_count;
623
624 /* UNLOCK */
625 pthread_mutex_unlock(&ps->lock);
626
627 return count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100628}
629
Michal Vasko428087d2016-01-14 16:04:28 +0100630/* must be called holding the session lock! */
631static NC_MSG_TYPE
632nc_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
633{
634 struct lyxml_elem *xml = NULL;
635 NC_MSG_TYPE msgtype;
636
637 if (!session || !rpc) {
638 ERRARG;
639 return NC_MSG_ERROR;
640 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100641 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100642 return NC_MSG_ERROR;
643 }
644
645 msgtype = nc_read_msg(session, &xml);
646
647 switch (msgtype) {
648 case NC_MSG_RPC:
649 *rpc = malloc(sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100650 if (!*rpc) {
651 ERRMEM;
652 goto error;
653 }
Michal Vaskoca4a2422016-02-02 12:17:14 +0100654
Michal Vasko428087d2016-01-14 16:04:28 +0100655 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child, LYD_OPT_DESTRUCT | LYD_OPT_RPC);
Michal Vaskoca4a2422016-02-02 12:17:14 +0100656 if (!(*rpc)->tree) {
657 ERR("Session %u: received message failed to be parsed into a known RPC.", session->id);
658 msgtype = NC_MSG_NONE;
659 }
Michal Vasko428087d2016-01-14 16:04:28 +0100660 (*rpc)->root = xml;
661 break;
662 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +0100663 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100664 goto error;
665 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +0100666 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100667 goto error;
668 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +0100669 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100670 goto error;
671 default:
672 /* NC_MSG_ERROR - pass it out;
673 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
674 */
675 break;
676 }
677
678 return msgtype;
679
680error:
681 /* cleanup */
682 lyxml_free(server_opts.ctx, xml);
683
684 return NC_MSG_ERROR;
685}
686
687/* must be called holding the session lock! */
688static NC_MSG_TYPE
689nc_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
690{
691 nc_rpc_clb clb;
692 struct nc_server_reply *reply;
693 int ret;
694
Michal Vasko4a827e52016-03-03 10:59:00 +0100695 if (!rpc) {
696 ERRINT;
697 return NC_MSG_ERROR;
698 }
699
Michal Vasko428087d2016-01-14 16:04:28 +0100700 /* no callback, reply with a not-implemented error */
Michal Vaskofd100c92016-03-01 15:23:46 +0100701 if (!rpc->tree || !rpc->tree->schema->priv) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100702 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +0100703 } else {
Michal Vaskofd100c92016-03-01 15:23:46 +0100704 clb = (nc_rpc_clb)rpc->tree->schema->priv;
Michal Vasko428087d2016-01-14 16:04:28 +0100705 reply = clb(rpc->tree, session);
706 }
707
708 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100709 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +0100710 }
711
712 ret = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
713
714 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
715 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
716 session->status = NC_STATUS_INVALID;
717 }
718
719 if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100720 ERR("Session %u: failed to write reply.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100721 nc_server_reply_free(reply);
722 return NC_MSG_ERROR;
723 }
724 nc_server_reply_free(reply);
725
726 return NC_MSG_REPLY;
727}
728
729API int
730nc_ps_poll(struct nc_pollsession *ps, int timeout)
731{
732 int ret;
Michal Vasko3512e402016-01-28 16:22:34 +0100733 uint16_t i;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100734 time_t cur_time;
Michal Vasko428087d2016-01-14 16:04:28 +0100735 NC_MSG_TYPE msgtype;
736 struct nc_session *session;
Michal Vasko4a827e52016-03-03 10:59:00 +0100737 struct nc_server_rpc *rpc = NULL;
Michal Vasko428087d2016-01-14 16:04:28 +0100738
739 if (!ps || !ps->session_count) {
740 ERRARG;
741 return -1;
742 }
743
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100744 cur_time = time(NULL);
745
Michal Vasko48a63ed2016-03-01 09:48:21 +0100746 /* LOCK */
747 pthread_mutex_lock(&ps->lock);
748
Michal Vasko428087d2016-01-14 16:04:28 +0100749 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100750 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
751 ERR("Session %u: session not running.", ps->sessions[i]->id);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100752 ret = -1;
753 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +0100754 }
Michal Vaskobd8ef262016-01-20 11:09:27 +0100755
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100756 /* TODO invalidate only sessions without subscription */
Michal Vasko3a715132016-01-21 15:40:31 +0100757 if (server_opts.idle_timeout && (ps->sessions[i]->last_rpc + server_opts.idle_timeout >= cur_time)) {
758 ERR("Session %u: session idle timeout elapsed.", ps->sessions[i]->id);
759 ps->sessions[i]->status = NC_STATUS_INVALID;
760 ps->sessions[i]->term_reason = NC_SESSION_TERM_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100761 ret = 3;
762 goto finish;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100763 }
764
Michal Vasko3a715132016-01-21 15:40:31 +0100765 if (ps->pfds[i].revents) {
Michal Vaskobd8ef262016-01-20 11:09:27 +0100766 break;
767 }
Michal Vasko428087d2016-01-14 16:04:28 +0100768 }
769
Michal Vaskobd8ef262016-01-20 11:09:27 +0100770 if (i == ps->session_count) {
Radek Krejci53691be2016-02-22 13:58:37 +0100771#ifdef NC_ENABLED_SSH
Michal Vasko3a715132016-01-21 15:40:31 +0100772retry_poll:
Michal Vasko3512e402016-01-28 16:22:34 +0100773#endif
Michal Vaskobd8ef262016-01-20 11:09:27 +0100774 /* no leftover event */
775 i = 0;
Michal Vasko3a715132016-01-21 15:40:31 +0100776 ret = poll(ps->pfds, ps->session_count, timeout);
Michal Vaskobd8ef262016-01-20 11:09:27 +0100777 if (ret < 1) {
Michal Vasko48a63ed2016-03-01 09:48:21 +0100778 goto finish;
Michal Vaskobd8ef262016-01-20 11:09:27 +0100779 }
Michal Vasko428087d2016-01-14 16:04:28 +0100780 }
781
Michal Vaskobd8ef262016-01-20 11:09:27 +0100782 /* find the first fd with POLLIN, we don't care if there are more now */
783 for (; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100784 if (ps->pfds[i].revents & POLLHUP) {
785 ERR("Session %u: communication socket unexpectedly closed.", ps->sessions[i]->id);
786 ps->sessions[i]->status = NC_STATUS_INVALID;
787 ps->sessions[i]->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100788 ret = 3;
789 goto finish;
Michal Vasko3a715132016-01-21 15:40:31 +0100790 } else if (ps->pfds[i].revents & POLLERR) {
791 ERR("Session %u: communication socket error.", ps->sessions[i]->id);
792 ps->sessions[i]->status = NC_STATUS_INVALID;
793 ps->sessions[i]->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100794 ret = 3;
795 goto finish;
Michal Vasko3a715132016-01-21 15:40:31 +0100796 } else if (ps->pfds[i].revents & POLLIN) {
Radek Krejci53691be2016-02-22 13:58:37 +0100797#ifdef NC_ENABLED_SSH
Michal Vasko96164bf2016-01-21 15:41:58 +0100798 if (ps->sessions[i]->ti_type == NC_TI_LIBSSH) {
Michal Vasko3512e402016-01-28 16:22:34 +0100799 uint16_t j;
800
Michal Vasko96164bf2016-01-21 15:41:58 +0100801 /* things are not that simple with SSH... */
Michal Vasko62be1ce2016-03-03 13:24:52 +0100802 ret = nc_ssh_pollin(ps->sessions[i], timeout);
Michal Vasko96164bf2016-01-21 15:41:58 +0100803
804 /* clear POLLIN on sessions sharing this session's SSH session */
805 if ((ret == 1) || (ret >= 4)) {
806 for (j = i + 1; j < ps->session_count; ++j) {
807 if (ps->pfds[j].fd == ps->pfds[i].fd) {
808 ps->pfds[j].revents = 0;
809 }
810 }
811 }
812
813 /* actual event happened */
814 if ((ret <= 0) || (ret >= 3)) {
815 ps->pfds[i].revents = 0;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100816 goto finish;
Michal Vasko96164bf2016-01-21 15:41:58 +0100817
818 /* event occurred on some other channel */
819 } else if (ret == 2) {
820 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100821 if (i == ps->session_count - 1) {
822 /* last session and it is not the right channel, ... */
Michal Vasko8c748832016-02-03 15:32:16 +0100823 if (!timeout) {
Michal Vasko428087d2016-01-14 16:04:28 +0100824 /* ... timeout is 0, so that is it */
Michal Vasko48a63ed2016-03-01 09:48:21 +0100825 ret = 0;
826 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +0100827 }
Michal Vasko8c748832016-02-03 15:32:16 +0100828 /* ... retry polling reasonable time apart ... */
829 usleep(NC_TIMEOUT_STEP);
830 if (timeout > 0) {
831 /* ... and decrease timeout, if not -1 */
Michal Vasko7b38e232016-02-26 15:01:07 +0100832 timeout -= NC_TIMEOUT_STEP * 1000;
Michal Vasko8c748832016-02-03 15:32:16 +0100833 }
834 goto retry_poll;
Michal Vasko428087d2016-01-14 16:04:28 +0100835 }
836 /* check other sessions */
837 continue;
Michal Vasko428087d2016-01-14 16:04:28 +0100838 }
839 }
Radek Krejci53691be2016-02-22 13:58:37 +0100840#endif /* NC_ENABLED_SSH */
Michal Vasko428087d2016-01-14 16:04:28 +0100841
Michal Vaskobd8ef262016-01-20 11:09:27 +0100842 /* we are going to process it now */
Michal Vasko3a715132016-01-21 15:40:31 +0100843 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100844 break;
845 }
846 }
847
848 if (i == ps->session_count) {
849 ERRINT;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100850 ret = -1;
851 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +0100852 }
853
854 /* this is the session with some data available for reading */
Michal Vasko3a715132016-01-21 15:40:31 +0100855 session = ps->sessions[i];
Michal Vasko428087d2016-01-14 16:04:28 +0100856
Michal Vaskobd8ef262016-01-20 11:09:27 +0100857 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal Vasko62be1ce2016-03-03 13:24:52 +0100858 ret = nc_timedlock(session->ti_lock, timeout);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100859 if (ret != 1) {
860 /* error or timeout */
Michal Vasko48a63ed2016-03-01 09:48:21 +0100861 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +0100862 }
863
864 msgtype = nc_recv_rpc(session, &rpc);
865 if (msgtype == NC_MSG_ERROR) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100866 pthread_mutex_unlock(session->ti_lock);
Michal Vasko428087d2016-01-14 16:04:28 +0100867 if (session->status != NC_STATUS_RUNNING) {
Michal Vasko48a63ed2016-03-01 09:48:21 +0100868 ret = 3;
869 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +0100870 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100871 ret = -1;
872 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +0100873 }
874
Michal Vaskoca4a2422016-02-02 12:17:14 +0100875 /* NC_MSG_NONE is not a real (known) RPC */
876 if (msgtype == NC_MSG_RPC) {
877 session->last_rpc = time(NULL);
878 }
879
Michal Vasko428087d2016-01-14 16:04:28 +0100880 /* process RPC */
881 msgtype = nc_send_reply(session, rpc);
882
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100883 pthread_mutex_unlock(session->ti_lock);
Michal Vasko428087d2016-01-14 16:04:28 +0100884
885 if (msgtype == NC_MSG_ERROR) {
Michal Vaskoca4a2422016-02-02 12:17:14 +0100886 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100887 ret = -1;
888 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +0100889 }
Michal Vaskoca4a2422016-02-02 12:17:14 +0100890 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vaskobd8ef262016-01-20 11:09:27 +0100891
Michal Vaskobd8b4e12016-01-22 16:11:20 +0100892 /* status change takes precedence over leftover events (return 2) */
893 if (session->status != NC_STATUS_RUNNING) {
Michal Vasko48a63ed2016-03-01 09:48:21 +0100894 ret = 3;
895 goto finish;
Michal Vaskobd8b4e12016-01-22 16:11:20 +0100896 }
897
Michal Vaskobd8ef262016-01-20 11:09:27 +0100898 /* is there some other socket waiting? */
899 for (++i; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100900 if (ps->pfds[i].revents) {
Michal Vasko48a63ed2016-03-01 09:48:21 +0100901 ret = 2;
902 goto finish;
Michal Vaskobd8ef262016-01-20 11:09:27 +0100903 }
904 }
905
Michal Vasko48a63ed2016-03-01 09:48:21 +0100906 ret = 1;
907
908finish:
909 /* UNLOCK */
910 pthread_mutex_unlock(&ps->lock);
911 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100912}
913
Michal Vaskod09eae62016-02-01 10:32:52 +0100914API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100915nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +0100916{
917 uint16_t i;
918 struct nc_session *session;
919
Michal Vasko9a25e932016-02-01 10:36:42 +0100920 if (!ps) {
921 ERRARG;
922 return;
923 }
924
Michal Vasko48a63ed2016-03-01 09:48:21 +0100925 /* LOCK */
926 pthread_mutex_lock(&ps->lock);
Michal Vaskod09eae62016-02-01 10:32:52 +0100927
Michal Vasko48a63ed2016-03-01 09:48:21 +0100928 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +0100929 for (i = 0; i < ps->session_count; i++) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100930 nc_session_free(ps->sessions[i], data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100931 }
932 free(ps->sessions);
933 ps->sessions = NULL;
934 free(ps->pfds);
935 ps->pfds = NULL;
936 ps->session_count = 0;
937 } else {
938 for (i = 0; i < ps->session_count; ) {
939 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
940 session = ps->sessions[i];
Radek Krejcid5f978f2016-03-03 13:14:45 +0100941 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +0100942 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100943 continue;
944 }
945
946 ++i;
947 }
Michal Vaskod09eae62016-02-01 10:32:52 +0100948 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100949
950 /* UNLOCK */
951 pthread_mutex_unlock(&ps->lock);
Michal Vaskod09eae62016-02-01 10:32:52 +0100952}
953
Radek Krejci53691be2016-02-22 13:58:37 +0100954#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +0100955
Michal Vasko3031aae2016-01-27 16:07:18 +0100956int
957nc_server_add_endpt_listen(const char *name, const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +0100958{
959 int sock;
Michal Vasko3031aae2016-01-27 16:07:18 +0100960 uint16_t i;
Radek Krejci53691be2016-02-22 13:58:37 +0100961#ifdef NC_ENABLED_SSH
Michal Vasko08a629a2016-02-02 12:20:47 +0100962 struct nc_server_ssh_opts *ssh_opts;
963#endif
Michal Vasko9e036d52016-01-08 10:49:26 +0100964
Michal Vasko3031aae2016-01-27 16:07:18 +0100965 if (!name || !address || !port) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100966 ERRARG;
967 return -1;
968 }
969
Michal Vasko51e514d2016-02-02 15:51:52 +0100970 /* WRITE LOCK */
971 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100972
973 /* check name uniqueness */
974 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskod4c03a82016-02-08 15:27:26 +0100975 if ((server_opts.binds[i].ti == ti) && !strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100976 ERR("Endpoint \"%s\" already exists.", name);
Michal Vasko51e514d2016-02-02 15:51:52 +0100977 /* WRITE UNLOCK */
Michal Vasko9faf1c82016-02-01 13:26:19 +0100978 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100979 return -1;
980 }
981 }
982
Michal Vasko9e036d52016-01-08 10:49:26 +0100983 sock = nc_sock_listen(address, port);
984 if (sock == -1) {
Michal Vasko51e514d2016-02-02 15:51:52 +0100985 /* WRITE UNLOCK */
986 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +0100987 return -1;
988 }
989
Michal Vasko3031aae2016-01-27 16:07:18 +0100990 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100991 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
992 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
993 if (!server_opts.binds || !server_opts.endpts) {
994 ERRMEM;
995 /* WRITE UNLOCK */
996 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko0f74da52016-03-03 08:52:52 +0100997 close(sock);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100998 return -1;
999 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001000
Michal Vasko3031aae2016-01-27 16:07:18 +01001001 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
1002 server_opts.binds[server_opts.endpt_count - 1].address = lydict_insert(server_opts.ctx, address, 0);
Michal Vasko3031aae2016-01-27 16:07:18 +01001003 server_opts.binds[server_opts.endpt_count - 1].port = port;
1004 server_opts.binds[server_opts.endpt_count - 1].sock = sock;
1005 server_opts.binds[server_opts.endpt_count - 1].ti = ti;
1006 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001007#ifdef NC_ENABLED_SSH
Michal Vasko3031aae2016-01-27 16:07:18 +01001008 case NC_TI_LIBSSH:
Michal Vasko08a629a2016-02-02 12:20:47 +01001009 ssh_opts = calloc(1, sizeof *ssh_opts);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001010 if (!ssh_opts) {
1011 ERRMEM;
1012 /* WRITE UNLOCK */
1013 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1014 return -1;
1015 }
Michal Vasko08a629a2016-02-02 12:20:47 +01001016 /* set default values */
1017 ssh_opts->auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1018 ssh_opts->auth_attempts = 3;
1019 ssh_opts->auth_timeout = 10;
1020
1021 server_opts.endpts[server_opts.endpt_count - 1].ti_opts = ssh_opts;
Michal Vasko3031aae2016-01-27 16:07:18 +01001022 break;
1023#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001024#ifdef NC_ENABLED_TLS
Michal Vasko3031aae2016-01-27 16:07:18 +01001025 case NC_TI_OPENSSL:
1026 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 +01001027 if (!server_opts.endpts[server_opts.endpt_count - 1].ti_opts) {
1028 ERRMEM;
1029 /* WRITE UNLOCK */
1030 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1031 return -1;
1032 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001033 break;
1034#endif
1035 default:
1036 ERRINT;
1037 server_opts.endpts[server_opts.endpt_count - 1].ti_opts = NULL;
1038 break;
1039 }
1040 pthread_mutex_init(&server_opts.endpts[server_opts.endpt_count - 1].endpt_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001041
Michal Vasko3031aae2016-01-27 16:07:18 +01001042 /* WRITE UNLOCK */
1043 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001044
Michal Vasko9e036d52016-01-08 10:49:26 +01001045 return 0;
1046}
1047
Michal Vasko3031aae2016-01-27 16:07:18 +01001048int
Michal Vaskoda514772016-02-01 11:32:01 +01001049nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
1050{
1051 struct nc_endpt *endpt;
1052 struct nc_bind *bind = NULL;
1053 uint16_t i;
1054 int sock;
1055
1056 if (!endpt_name || (!address && !port) || (address && port) || !ti) {
1057 ERRARG;
1058 return -1;
1059 }
1060
Michal Vasko51e514d2016-02-02 15:51:52 +01001061 /* LOCK */
Michal Vaskoda514772016-02-01 11:32:01 +01001062 endpt = nc_server_endpt_lock(endpt_name, ti);
1063 if (!endpt) {
1064 return -1;
1065 }
1066
1067 /* we need to learn the index, to get the bind :-/ */
1068 for (i = 0; i < server_opts.endpt_count; ++i) {
1069 if (&server_opts.endpts[i] == endpt) {
1070 bind = &server_opts.binds[i];
1071 }
1072 }
1073 if (!bind) {
1074 ERRINT;
Michal Vasko51e514d2016-02-02 15:51:52 +01001075 goto fail;
Michal Vaskoda514772016-02-01 11:32:01 +01001076 }
1077
1078 if (address) {
1079 sock = nc_sock_listen(address, bind->port);
1080 } else {
1081 sock = nc_sock_listen(bind->address, port);
1082 }
1083 if (sock == -1) {
Michal Vasko51e514d2016-02-02 15:51:52 +01001084 goto fail;
Michal Vaskoda514772016-02-01 11:32:01 +01001085 }
1086
1087 /* close old socket, update parameters */
1088 close(bind->sock);
1089 bind->sock = sock;
1090 if (address) {
1091 lydict_remove(server_opts.ctx, bind->address);
1092 bind->address = lydict_insert(server_opts.ctx, address, 0);
1093 } else {
1094 bind->port = port;
1095 }
1096
Michal Vasko51e514d2016-02-02 15:51:52 +01001097 /* UNLOCK */
Michal Vasko7a93af72016-02-01 16:00:15 +01001098 nc_server_endpt_unlock(endpt);
Michal Vaskoda514772016-02-01 11:32:01 +01001099 return 0;
Michal Vasko51e514d2016-02-02 15:51:52 +01001100
1101fail:
1102 /* UNLOCK */
1103 nc_server_endpt_unlock(endpt);
1104 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001105}
1106
1107int
Michal Vasko3031aae2016-01-27 16:07:18 +01001108nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001109{
1110 uint32_t i;
1111 int ret = -1;
1112
Michal Vasko3031aae2016-01-27 16:07:18 +01001113 /* WRITE LOCK */
1114 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001115
Michal Vasko3031aae2016-01-27 16:07:18 +01001116 if (!name && !ti) {
1117 /* remove all */
Michal Vasko3031aae2016-01-27 16:07:18 +01001118 for (i = 0; i < server_opts.endpt_count; ++i) {
1119 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko11d142a2016-01-19 15:58:24 +01001120 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
Michal Vasko51e514d2016-02-02 15:51:52 +01001121
Michal Vasko3031aae2016-01-27 16:07:18 +01001122 close(server_opts.binds[i].sock);
1123 pthread_mutex_destroy(&server_opts.endpts[i].endpt_lock);
1124 switch (server_opts.binds[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001125#ifdef NC_ENABLED_SSH
Michal Vasko3031aae2016-01-27 16:07:18 +01001126 case NC_TI_LIBSSH:
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001127 nc_server_ssh_clear_opts(server_opts.endpts[i].ti_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001128 break;
1129#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001130#ifdef NC_ENABLED_TLS
Michal Vasko3031aae2016-01-27 16:07:18 +01001131 case NC_TI_OPENSSL:
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001132 nc_server_tls_clear_opts(server_opts.endpts[i].ti_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001133 break;
1134#endif
1135 default:
1136 ERRINT;
1137 break;
1138 }
1139 free(server_opts.endpts[i].ti_opts);
Michal Vasko9e036d52016-01-08 10:49:26 +01001140
Michal Vasko9e036d52016-01-08 10:49:26 +01001141 ret = 0;
1142 }
Michal Vasko7ddc5702016-02-08 15:29:39 +01001143 free(server_opts.binds);
1144 server_opts.binds = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +01001145 free(server_opts.endpts);
1146 server_opts.endpts = NULL;
1147 server_opts.endpt_count = 0;
1148
Michal Vasko1a38c862016-01-15 15:50:07 +01001149 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +01001150 /* remove one name endpoint or all ti endpoints */
1151 for (i = 0; i < server_opts.endpt_count; ++i) {
1152 if ((server_opts.binds[i].ti == ti) &&
1153 (!name || !strcmp(server_opts.endpts[i].name, name))) {
1154
Michal Vasko3031aae2016-01-27 16:07:18 +01001155 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko11d142a2016-01-19 15:58:24 +01001156 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
Michal Vasko3031aae2016-01-27 16:07:18 +01001157 close(server_opts.binds[i].sock);
1158 pthread_mutex_destroy(&server_opts.endpts[i].endpt_lock);
1159 switch (server_opts.binds[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001160#ifdef NC_ENABLED_SSH
Michal Vasko3031aae2016-01-27 16:07:18 +01001161 case NC_TI_LIBSSH:
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001162 nc_server_ssh_clear_opts(server_opts.endpts[i].ti_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001163 break;
1164#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001165#ifdef NC_ENABLED_TLS
Michal Vasko3031aae2016-01-27 16:07:18 +01001166 case NC_TI_OPENSSL:
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001167 nc_server_tls_clear_opts(server_opts.endpts[i].ti_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001168 break;
1169#endif
1170 default:
1171 ERRINT;
1172 break;
1173 }
1174 free(server_opts.endpts[i].ti_opts);
Michal Vasko1a38c862016-01-15 15:50:07 +01001175
Michal Vasko3031aae2016-01-27 16:07:18 +01001176 --server_opts.endpt_count;
Michal Vaskoc0256492016-02-02 12:19:06 +01001177 if (i < server_opts.endpt_count) {
1178 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1179 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
1180 } else if (!server_opts.endpt_count) {
1181 free(server_opts.binds);
1182 server_opts.binds = NULL;
1183 free(server_opts.endpts);
1184 server_opts.endpts = NULL;
1185 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001186
1187 ret = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001188
1189 if (name) {
1190 /* one name endpoint removed, they are unique, we're done */
1191 break;
1192 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001193 }
1194 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001195 }
1196
Michal Vasko3031aae2016-01-27 16:07:18 +01001197 /* WRITE UNLOCK */
1198 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001199
Michal Vasko9e036d52016-01-08 10:49:26 +01001200 return ret;
1201}
1202
Michal Vasko1a38c862016-01-15 15:50:07 +01001203API int
1204nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001205{
Michal Vasko1a38c862016-01-15 15:50:07 +01001206 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01001207 char *host = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +01001208 uint16_t port, idx;
Michal Vasko9e036d52016-01-08 10:49:26 +01001209
Michal Vasko51e514d2016-02-02 15:51:52 +01001210 if (!server_opts.ctx || !session) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001211 ERRARG;
Michal Vasko1a38c862016-01-15 15:50:07 +01001212 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001213 }
1214
Michal Vasko51e514d2016-02-02 15:51:52 +01001215 /* we have to hold WRITE for the whole time, since there is not
1216 * a way of downgrading the lock to READ */
1217 /* WRITE LOCK */
1218 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
1219
1220 if (!server_opts.endpt_count) {
1221 ERRARG;
1222 /* WRITE UNLOCK */
1223 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1224 return -1;
1225 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001226
Michal Vasko3031aae2016-01-27 16:07:18 +01001227 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &idx);
Michal Vaskob48aa812016-01-18 14:13:09 +01001228
Michal Vasko50456e82016-02-02 12:16:08 +01001229 if (ret < 1) {
Michal Vasko51e514d2016-02-02 15:51:52 +01001230 /* WRITE UNLOCK */
Michal Vasko3031aae2016-01-27 16:07:18 +01001231 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01001232 free(host);
Michal Vaskob48aa812016-01-18 14:13:09 +01001233 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001234 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001235 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001236
Michal Vasko1a38c862016-01-15 15:50:07 +01001237 *session = calloc(1, sizeof **session);
Michal Vasko686aa312016-01-21 15:58:18 +01001238 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001239 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001240 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01001241 free(host);
Michal Vasko3031aae2016-01-27 16:07:18 +01001242 ret = -1;
1243 goto fail;
Michal Vasko9e036d52016-01-08 10:49:26 +01001244 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001245 (*session)->status = NC_STATUS_STARTING;
1246 (*session)->side = NC_SERVER;
1247 (*session)->ctx = server_opts.ctx;
1248 (*session)->flags = NC_SESSION_SHAREDCTX;
1249 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
1250 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01001251
1252 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +01001253 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1254 if (!(*session)->ti_lock) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001255 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001256 close(sock);
Michal Vasko1a38c862016-01-15 15:50:07 +01001257 ret = -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001258 goto fail;
1259 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001260 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001261
Michal Vasko2cc4c682016-03-01 09:16:48 +01001262 (*session)->data = server_opts.endpts[idx].ti_opts;
Michal Vasko3031aae2016-01-27 16:07:18 +01001263
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001264 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001265#ifdef NC_ENABLED_SSH
Michal Vasko3031aae2016-01-27 16:07:18 +01001266 if (server_opts.binds[idx].ti == NC_TI_LIBSSH) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001267 ret = nc_accept_ssh_session(*session, sock, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001268 if (ret < 1) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001269 goto fail;
1270 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001271 } else
1272#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001273#ifdef NC_ENABLED_TLS
Michal Vasko3d865d22016-01-28 16:00:53 +01001274 if (server_opts.binds[idx].ti == NC_TI_OPENSSL) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001275 ret = nc_accept_tls_session(*session, sock, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001276 if (ret < 1) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001277 goto fail;
1278 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001279 } else
1280#endif
1281 {
Michal Vasko9e036d52016-01-08 10:49:26 +01001282 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001283 close(sock);
Michal Vasko1a38c862016-01-15 15:50:07 +01001284 ret = -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001285 goto fail;
1286 }
1287
Michal Vasko2cc4c682016-03-01 09:16:48 +01001288 (*session)->data = NULL;
1289
Michal Vasko51e514d2016-02-02 15:51:52 +01001290 /* WRITE UNLOCK */
Michal Vasko3031aae2016-01-27 16:07:18 +01001291 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1292
Michal Vaskob48aa812016-01-18 14:13:09 +01001293 /* assign new SID atomically */
1294 /* LOCK */
1295 pthread_spin_lock(&server_opts.sid_lock);
1296 (*session)->id = server_opts.new_session_id++;
1297 /* UNLOCK */
1298 pthread_spin_unlock(&server_opts.sid_lock);
1299
Michal Vasko9e036d52016-01-08 10:49:26 +01001300 /* NETCONF handshake */
Michal Vasko1a38c862016-01-15 15:50:07 +01001301 if (nc_handshake(*session)) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001302 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001303 *session = NULL;
1304 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001305 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001306 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01001307
Michal Vasko1a38c862016-01-15 15:50:07 +01001308 return 1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001309
1310fail:
Michal Vasko3031aae2016-01-27 16:07:18 +01001311 /* WRITE UNLOCK */
1312 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1313
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001314 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001315 *session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001316 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001317}
1318
Michal Vasko3031aae2016-01-27 16:07:18 +01001319int
Michal Vasko8f5270d2016-02-29 16:22:25 +01001320nc_connect_callhome(const char *host, uint16_t port, NC_TRANSPORT_IMPL ti, struct nc_session **session)
Michal Vaskob05053d2016-01-22 16:12:06 +01001321{
1322 int sock, ret;
1323
Michal Vaskoc61c4492016-01-25 11:13:34 +01001324 if (!host || !port || !ti || !session) {
1325 ERRARG;
1326 return -1;
1327 }
1328
Michal Vaskob05053d2016-01-22 16:12:06 +01001329 sock = nc_sock_connect(host, port);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001330 if (sock < 0) {
1331 return -1;
Michal Vaskob05053d2016-01-22 16:12:06 +01001332 }
1333
1334 *session = calloc(1, sizeof **session);
1335 if (!(*session)) {
1336 ERRMEM;
1337 close(sock);
1338 return -1;
1339 }
1340 (*session)->status = NC_STATUS_STARTING;
1341 (*session)->side = NC_SERVER;
1342 (*session)->ctx = server_opts.ctx;
1343 (*session)->flags = NC_SESSION_SHAREDCTX | NC_SESSION_CALLHOME;
Michal Vaskob05053d2016-01-22 16:12:06 +01001344 (*session)->host = lydict_insert(server_opts.ctx, host, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +01001345 (*session)->port = port;
1346
1347 /* transport lock */
1348 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1349 if (!(*session)->ti_lock) {
1350 ERRMEM;
1351 close(sock);
1352 ret = -1;
1353 goto fail;
1354 }
1355 pthread_mutex_init((*session)->ti_lock, NULL);
1356
1357 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001358#ifdef NC_ENABLED_SSH
Michal Vaskob05053d2016-01-22 16:12:06 +01001359 if (ti == NC_TI_LIBSSH) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001360 /* OPTS LOCK */
1361 pthread_mutex_lock(&ssh_ch_opts_lock);
1362
Michal Vasko2cc4c682016-03-01 09:16:48 +01001363 (*session)->data = &ssh_ch_opts;
Michal Vasko0190bc32016-03-02 15:47:49 +01001364 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01001365 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001366
1367 /* OPTS UNLOCK */
1368 pthread_mutex_unlock(&ssh_ch_opts_lock);
1369
Michal Vaskob05053d2016-01-22 16:12:06 +01001370 if (ret < 1) {
1371 goto fail;
1372 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001373 } else
1374#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001375#ifdef NC_ENABLED_TLS
Michal Vasko3d865d22016-01-28 16:00:53 +01001376 if (ti == NC_TI_OPENSSL) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001377 /* OPTS LOCK */
1378 pthread_mutex_lock(&tls_ch_opts_lock);
1379
Michal Vasko2cc4c682016-03-01 09:16:48 +01001380 (*session)->data = &tls_ch_opts;
Michal Vasko0190bc32016-03-02 15:47:49 +01001381 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01001382 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001383
1384 /* OPTS UNLOCK */
1385 pthread_mutex_unlock(&tls_ch_opts_lock);
1386
Michal Vaskob05053d2016-01-22 16:12:06 +01001387 if (ret < 1) {
1388 goto fail;
1389 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001390 } else
1391#endif
1392 {
Michal Vaskob05053d2016-01-22 16:12:06 +01001393 ERRINT;
1394 close(sock);
1395 ret = -1;
1396 goto fail;
1397 }
1398
1399 /* assign new SID atomically */
1400 /* LOCK */
1401 pthread_spin_lock(&server_opts.sid_lock);
1402 (*session)->id = server_opts.new_session_id++;
1403 /* UNLOCK */
1404 pthread_spin_unlock(&server_opts.sid_lock);
1405
1406 /* NETCONF handshake */
1407 if (nc_handshake(*session)) {
1408 ret = -1;
1409 goto fail;
1410 }
1411 (*session)->status = NC_STATUS_RUNNING;
1412
1413 return 1;
1414
1415fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001416 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01001417 *session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001418 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +01001419}
1420
Radek Krejci53691be2016-02-22 13:58:37 +01001421#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */