blob: dc0ce84fbe3fab241d582cb4b6717a8841f76e64 [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 Vasko2e6defd2016-10-07 15:48:15 +020033 .endpt_lock = PTHREAD_RWLOCK_INITIALIZER,
34 .ch_client_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010035};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010036
fanchanghu966f2de2016-07-21 02:28:57 -040037static nc_rpc_clb global_rpc_clb = NULL;
38
Michal Vasko3031aae2016-01-27 16:07:18 +010039struct nc_endpt *
Michal Vasko2e6defd2016-10-07 15:48:15 +020040nc_server_endpt_lock(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
Michal Vasko3031aae2016-01-27 16:07:18 +010041{
42 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010043 struct nc_endpt *endpt = NULL;
44
45 /* READ LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020046 pthread_rwlock_rdlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010047
48 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko2e6defd2016-10-07 15:48:15 +020049 if (!strcmp(server_opts.endpts[i].name, name) && (!ti || (server_opts.endpts[i].ti == ti))) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010050 endpt = &server_opts.endpts[i];
51 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010052 }
53 }
54
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010055 if (!endpt) {
56 ERR("Endpoint \"%s\" was not found.", name);
57 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020058 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010059 return NULL;
60 }
61
62 /* ENDPT LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020063 pthread_mutex_lock(&endpt->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010064
Michal Vaskoe2713da2016-08-22 16:06:40 +020065 if (idx) {
66 *idx = i;
67 }
68
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010069 return endpt;
70}
71
72void
73nc_server_endpt_unlock(struct nc_endpt *endpt)
74{
75 /* ENDPT UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020076 pthread_mutex_unlock(&endpt->lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010077
78 /* READ UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020079 pthread_rwlock_unlock(&server_opts.endpt_lock);
80}
81
82struct nc_ch_client *
83nc_server_ch_client_lock(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx)
84{
85 uint16_t i;
86 struct nc_ch_client *client = NULL;
87
88 /* READ LOCK */
89 pthread_rwlock_rdlock(&server_opts.ch_client_lock);
90
91 for (i = 0; i < server_opts.ch_client_count; ++i) {
92 if (!strcmp(server_opts.ch_clients[i].name, name) && (!ti || (server_opts.ch_clients[i].ti == ti))) {
93 client = &server_opts.ch_clients[i];
94 break;
95 }
96 }
97
98 if (!client) {
99 ERR("Call Home client \"%s\" was not found.", name);
100 /* READ UNLOCK */
101 pthread_rwlock_unlock(&server_opts.ch_client_lock);
102 return NULL;
103 }
104
105 /* CH CLIENT LOCK */
106 pthread_mutex_lock(&client->lock);
107
108 if (idx) {
109 *idx = i;
110 }
111
112 return client;
113}
114
115void
116nc_server_ch_client_unlock(struct nc_ch_client *client)
117{
118 /* CH CLIENT UNLOCK */
119 pthread_mutex_unlock(&client->lock);
120
121 /* READ UNLOCK */
122 pthread_rwlock_unlock(&server_opts.ch_client_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100123}
Michal Vasko086311b2016-01-08 09:53:11 +0100124
Michal Vasko1a38c862016-01-15 15:50:07 +0100125API void
126nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
127{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200128 if (!session) {
129 ERRARG("session");
130 return;
131 } else if (!reason) {
132 ERRARG("reason");
Michal Vasko1a38c862016-01-15 15:50:07 +0100133 return;
134 }
135
136 session->term_reason = reason;
137}
138
Michal Vasko086311b2016-01-08 09:53:11 +0100139int
Michal Vaskof05562c2016-01-20 12:06:43 +0100140nc_sock_listen(const char *address, uint16_t port)
Michal Vasko086311b2016-01-08 09:53:11 +0100141{
142 const int optVal = 1;
143 const socklen_t optLen = sizeof(optVal);
144 int is_ipv4, sock;
145 struct sockaddr_storage saddr;
146
147 struct sockaddr_in *saddr4;
148 struct sockaddr_in6 *saddr6;
149
150
151 if (!strchr(address, ':')) {
152 is_ipv4 = 1;
153 } else {
154 is_ipv4 = 0;
155 }
156
157 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
158 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100159 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100160 goto fail;
161 }
162
163 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&optVal, optLen)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100164 ERR("Could not set socket SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100165 goto fail;
166 }
167
168 bzero(&saddr, sizeof(struct sockaddr_storage));
169 if (is_ipv4) {
170 saddr4 = (struct sockaddr_in *)&saddr;
171
172 saddr4->sin_family = AF_INET;
173 saddr4->sin_port = htons(port);
174
175 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100176 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100177 goto fail;
178 }
179
180 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100181 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100182 goto fail;
183 }
184
185 } else {
186 saddr6 = (struct sockaddr_in6 *)&saddr;
187
188 saddr6->sin6_family = AF_INET6;
189 saddr6->sin6_port = htons(port);
190
191 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100192 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100193 goto fail;
194 }
195
196 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100197 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100198 goto fail;
199 }
200 }
201
Michal Vaskofb89d772016-01-08 12:25:35 +0100202 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100203 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100204 goto fail;
205 }
206
207 return sock;
208
209fail:
210 if (sock > -1) {
211 close(sock);
212 }
213
214 return -1;
215}
216
217int
Michal Vasko3031aae2016-01-27 16:07:18 +0100218nc_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 +0100219{
220 uint16_t i;
221 struct pollfd *pfd;
222 struct sockaddr_storage saddr;
223 socklen_t saddr_len = sizeof(saddr);
Michal Vasko0190bc32016-03-02 15:47:49 +0100224 int ret, sock = -1, flags;
Michal Vasko086311b2016-01-08 09:53:11 +0100225
226 pfd = malloc(bind_count * sizeof *pfd);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100227 if (!pfd) {
228 ERRMEM;
229 return -1;
230 }
231
Michal Vasko94acafc2016-09-23 13:40:10 +0200232 i = 0;
233 while (i < bind_count) {
234 if (binds[i].sock < 0) {
235 /* invalid socket */
236 --bind_count;
237 continue;
238 }
Michal Vasko086311b2016-01-08 09:53:11 +0100239 pfd[i].fd = binds[i].sock;
240 pfd[i].events = POLLIN;
241 pfd[i].revents = 0;
Michal Vasko94acafc2016-09-23 13:40:10 +0200242
243 ++i;
Michal Vasko086311b2016-01-08 09:53:11 +0100244 }
245
246 /* poll for a new connection */
247 errno = 0;
248 ret = poll(pfd, bind_count, timeout);
249 if (!ret) {
250 /* we timeouted */
251 free(pfd);
252 return 0;
253 } else if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100254 ERR("Poll failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100255 free(pfd);
256 return -1;
257 }
258
259 for (i = 0; i < bind_count; ++i) {
260 if (pfd[i].revents & POLLIN) {
261 sock = pfd[i].fd;
262 break;
263 }
264 }
265 free(pfd);
266
267 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100268 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100269 return -1;
270 }
271
272 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100273 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100274 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100275 return -1;
276 }
277
Michal Vasko0190bc32016-03-02 15:47:49 +0100278 /* make the socket non-blocking */
279 if (((flags = fcntl(ret, F_GETFL)) == -1) || (fcntl(ret, F_SETFL, flags | O_NONBLOCK) == -1)) {
280 ERR("Fcntl failed (%s).", strerror(errno));
Michal Vasko0f74da52016-03-03 08:52:52 +0100281 close(ret);
Michal Vasko0190bc32016-03-02 15:47:49 +0100282 return -1;
283 }
284
Michal Vasko3031aae2016-01-27 16:07:18 +0100285 if (idx) {
286 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100287 }
288
Michal Vasko086311b2016-01-08 09:53:11 +0100289 /* host was requested */
290 if (host) {
291 if (saddr.ss_family == AF_INET) {
292 *host = malloc(15);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100293 if (*host) {
294 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, 15)) {
295 ERR("inet_ntop failed (%s).", strerror(errno));
296 free(*host);
297 *host = NULL;
298 }
Michal Vasko086311b2016-01-08 09:53:11 +0100299
Michal Vasko4eb3c312016-03-01 14:09:37 +0100300 if (port) {
301 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
302 }
303 } else {
304 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100305 }
306 } else if (saddr.ss_family == AF_INET6) {
307 *host = malloc(40);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100308 if (*host) {
309 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, 40)) {
310 ERR("inet_ntop failed (%s).", strerror(errno));
311 free(*host);
312 *host = NULL;
313 }
Michal Vasko086311b2016-01-08 09:53:11 +0100314
Michal Vasko4eb3c312016-03-01 14:09:37 +0100315 if (port) {
316 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
317 }
318 } else {
319 ERRMEM;
Michal Vasko086311b2016-01-08 09:53:11 +0100320 }
321 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100322 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100323 }
324 }
325
326 return ret;
327}
328
Michal Vasko05ba9df2016-01-13 14:40:27 +0100329static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100330nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100331{
332 const char *identifier = NULL, *version = NULL, *format = NULL;
333 char *model_data = NULL;
334 const struct lys_module *module;
335 struct nc_server_error *err;
336 struct lyd_node *child, *data = NULL;
Michal Vasko11d142a2016-01-19 15:58:24 +0100337 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100338
339 LY_TREE_FOR(rpc->child, child) {
340 if (!strcmp(child->schema->name, "identifier")) {
341 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
342 } else if (!strcmp(child->schema->name, "version")) {
343 version = ((struct lyd_node_leaf_list *)child)->value_str;
344 } else if (!strcmp(child->schema->name, "format")) {
345 format = ((struct lyd_node_leaf_list *)child)->value_str;
346 }
347 }
348
349 /* check version */
350 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100351 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
352 nc_err_set_msg(err, "The requested version is not supported.", "en");
353 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100354 }
355
356 /* check and get module with the name identifier */
357 module = ly_ctx_get_module(server_opts.ctx, identifier, version);
358 if (!module) {
Michal Vaskod91f6e62016-04-05 11:34:22 +0200359 module = (const struct lys_module *)ly_ctx_get_submodule(server_opts.ctx, NULL, NULL, identifier, version);
360 }
361 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100362 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
363 nc_err_set_msg(err, "The requested schema was not found.", "en");
364 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100365 }
366
367 /* check format */
368 if (!format || !strcmp(format, "yang")) {
369 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL);
370 } else if (!strcmp(format, "yin")) {
371 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL);
372 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100373 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
374 nc_err_set_msg(err, "The requested format is not supported.", "en");
375 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100376 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200377 if (!model_data) {
378 ERRINT;
379 return NULL;
380 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100381
Michal Vasko303245c2016-03-24 15:20:03 +0100382 sdata = ly_ctx_get_node(server_opts.ctx, NULL, "/ietf-netconf-monitoring:get-schema/output/data");
Michal Vaskod91f6e62016-04-05 11:34:22 +0200383 if (!sdata) {
384 ERRINT;
385 free(model_data);
386 return NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100387 }
Michal Vaskod91f6e62016-04-05 11:34:22 +0200388
Radek Krejci539efb62016-08-24 15:05:16 +0200389 data = lyd_new_path(NULL, server_opts.ctx, "/ietf-netconf-monitoring:get-schema/data", model_data,
390 LYD_ANYDATA_STRING, LYD_PATH_OPT_OUTPUT);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100391 if (!data) {
392 ERRINT;
Michal Vaskod91f6e62016-04-05 11:34:22 +0200393 free(model_data);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100394 return NULL;
395 }
396
Radek Krejci36dfdb32016-09-01 16:56:35 +0200397 return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100398}
399
400static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100401nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100402{
Michal Vasko428087d2016-01-14 16:04:28 +0100403 session->term_reason = NC_SESSION_TERM_CLOSED;
404 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100405}
406
Michal Vasko086311b2016-01-08 09:53:11 +0100407API int
408nc_server_init(struct ly_ctx *ctx)
409{
Michal Vasko05ba9df2016-01-13 14:40:27 +0100410 const struct lys_node *rpc;
411
Michal Vasko086311b2016-01-08 09:53:11 +0100412 if (!ctx) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200413 ERRARG("ctx");
Michal Vasko086311b2016-01-08 09:53:11 +0100414 return -1;
415 }
416
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100417 nc_init();
418
Michal Vasko05ba9df2016-01-13 14:40:27 +0100419 /* set default <get-schema> callback if not specified */
Michal Vasko303245c2016-03-24 15:20:03 +0100420 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf-monitoring:get-schema");
Michal Vaskofd100c92016-03-01 15:23:46 +0100421 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100422 lys_set_private(rpc, nc_clb_default_get_schema);
423 }
424
425 /* set default <close-session> callback if not specififed */
Michal Vasko303245c2016-03-24 15:20:03 +0100426 rpc = ly_ctx_get_node(ctx, NULL, "/ietf-netconf:close-session");
Michal Vaskofd100c92016-03-01 15:23:46 +0100427 if (rpc && !rpc->priv) {
Michal Vasko05ba9df2016-01-13 14:40:27 +0100428 lys_set_private(rpc, nc_clb_default_close_session);
429 }
430
Michal Vasko086311b2016-01-08 09:53:11 +0100431 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100432
433 server_opts.new_session_id = 1;
434 pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
435
Michal Vasko086311b2016-01-08 09:53:11 +0100436 return 0;
437}
438
Michal Vaskob48aa812016-01-18 14:13:09 +0100439API void
440nc_server_destroy(void)
441{
442 pthread_spin_destroy(&server_opts.sid_lock);
443
Radek Krejci53691be2016-02-22 13:58:37 +0100444#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200445 nc_server_del_endpt(NULL);
Michal Vaskob48aa812016-01-18 14:13:09 +0100446#endif
Michal Vaskoa7b8ca52016-03-01 12:09:29 +0100447 nc_destroy();
Michal Vaskob48aa812016-01-18 14:13:09 +0100448}
449
Michal Vasko086311b2016-01-08 09:53:11 +0100450API int
451nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
452{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200453 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) {
454 ERRARG("basic_mode");
455 return -1;
456 } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) {
457 ERRARG("also_supported");
Michal Vasko086311b2016-01-08 09:53:11 +0100458 return -1;
459 }
460
461 server_opts.wd_basic_mode = basic_mode;
462 server_opts.wd_also_supported = also_supported;
463 return 0;
464}
465
Michal Vasko1a38c862016-01-15 15:50:07 +0100466API void
Michal Vasko55f03972016-04-13 08:56:01 +0200467nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
468{
469 if (!basic_mode && !also_supported) {
470 ERRARG("basic_mode and also_supported");
471 return;
472 }
473
474 if (basic_mode) {
475 *basic_mode = server_opts.wd_basic_mode;
476 }
477 if (also_supported) {
478 *also_supported = server_opts.wd_also_supported;
479 }
480}
481
482API void
Michal Vasko086311b2016-01-08 09:53:11 +0100483nc_server_set_capab_interleave(int interleave_support)
484{
485 if (interleave_support) {
486 server_opts.interleave_capab = 1;
487 } else {
488 server_opts.interleave_capab = 0;
489 }
Michal Vasko086311b2016-01-08 09:53:11 +0100490}
491
Michal Vasko55f03972016-04-13 08:56:01 +0200492API int
493nc_server_get_capab_interleave(void)
494{
495 return server_opts.interleave_capab;
496}
497
Michal Vasko1a38c862016-01-15 15:50:07 +0100498API void
Michal Vasko086311b2016-01-08 09:53:11 +0100499nc_server_set_hello_timeout(uint16_t hello_timeout)
500{
Michal Vasko086311b2016-01-08 09:53:11 +0100501 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100502}
503
Michal Vasko55f03972016-04-13 08:56:01 +0200504API uint16_t
505nc_server_get_hello_timeout(void)
506{
507 return server_opts.hello_timeout;
508}
509
Michal Vasko1a38c862016-01-15 15:50:07 +0100510API void
Michal Vasko086311b2016-01-08 09:53:11 +0100511nc_server_set_idle_timeout(uint16_t idle_timeout)
512{
Michal Vasko086311b2016-01-08 09:53:11 +0100513 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100514}
515
Michal Vasko55f03972016-04-13 08:56:01 +0200516API uint16_t
517nc_server_get_idle_timeout(void)
518{
519 return server_opts.idle_timeout;
520}
521
Michal Vasko71090fc2016-05-24 16:37:28 +0200522API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +0100523nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100524{
Michal Vasko71090fc2016-05-24 16:37:28 +0200525 NC_MSG_TYPE msgtype;
526
Michal Vasko45e53ae2016-04-07 11:46:03 +0200527 if (!server_opts.ctx) {
528 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +0200529 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200530 } else if (fdin < 0) {
531 ERRARG("fdin");
Michal Vasko71090fc2016-05-24 16:37:28 +0200532 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200533 } else if (fdout < 0) {
534 ERRARG("fdout");
Michal Vasko71090fc2016-05-24 16:37:28 +0200535 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200536 } else if (!username) {
537 ERRARG("username");
Michal Vasko71090fc2016-05-24 16:37:28 +0200538 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200539 } else if (!session) {
540 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200541 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100542 }
543
544 /* prepare session structure */
Michal Vasko1a38c862016-01-15 15:50:07 +0100545 *session = calloc(1, sizeof **session);
546 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100547 ERRMEM;
Michal Vasko71090fc2016-05-24 16:37:28 +0200548 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +0100549 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100550 (*session)->status = NC_STATUS_STARTING;
551 (*session)->side = NC_SERVER;
Michal Vasko086311b2016-01-08 09:53:11 +0100552
553 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100554 (*session)->ti_type = NC_TI_FD;
555 (*session)->ti.fd.in = fdin;
556 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100557
558 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100559 (*session)->flags = NC_SESSION_SHAREDCTX;
560 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100561
Michal Vaskob48aa812016-01-18 14:13:09 +0100562 /* assign new SID atomically */
563 pthread_spin_lock(&server_opts.sid_lock);
564 (*session)->id = server_opts.new_session_id++;
565 pthread_spin_unlock(&server_opts.sid_lock);
566
Michal Vasko086311b2016-01-08 09:53:11 +0100567 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +0200568 msgtype = nc_handshake(*session);
569 if (msgtype != NC_MSG_HELLO) {
570 nc_session_free(*session, NULL);
571 *session = NULL;
572 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100573 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200574 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +0100575 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko086311b2016-01-08 09:53:11 +0100576
Michal Vasko71090fc2016-05-24 16:37:28 +0200577 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +0100578}
Michal Vasko9e036d52016-01-08 10:49:26 +0100579
Michal Vaskob30b99c2016-07-26 11:35:43 +0200580static void
581nc_ps_queue_remove_id(struct nc_pollsession *ps, uint8_t id)
582{
583 uint8_t i, found = 0;
584
585 for (i = 0; i < ps->queue_len; ++i) {
586 /* idx round buffer adjust */
587 if (ps->queue_begin + i == NC_PS_QUEUE_SIZE) {
588 i = -ps->queue_begin;
589 }
590
591 if (found) {
592 /* move the value back one place */
593 if (ps->queue[ps->queue_begin + i] == id) {
594 /* another equal value, simply cannot be */
595 ERRINT;
596 }
597
598 if (ps->queue_begin + i == 0) {
599 ps->queue[NC_PS_QUEUE_SIZE - 1] = ps->queue[ps->queue_begin + i];
600 } else {
601 ps->queue[ps->queue_begin + i - 1] = ps->queue[ps->queue_begin + i];
602 }
603 } else if (ps->queue[ps->queue_begin + i] == id) {
604 /* found our id, there can be no more equal valid values */
605 found = 1;
606 }
607 }
608
609 if (!found) {
610 ERRINT;
611 }
612 --ps->queue_len;
613}
614
Michal Vaskof04a52a2016-04-07 10:52:10 +0200615int
Michal Vasko26043172016-07-26 14:08:59 +0200616nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200617{
618 int ret;
Michal Vaskob30b99c2016-07-26 11:35:43 +0200619 uint8_t queue_last;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200620 struct timespec ts;
621
Radek Krejci7ac16052016-07-15 11:48:18 +0200622 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200623 ts.tv_sec += NC_READ_TIMEOUT;
624
625 /* LOCK */
626 ret = pthread_mutex_timedlock(&ps->lock, &ts);
627 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200628 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200629 return -1;
630 }
631
632 /* get a unique queue value (by adding 1 to the last added value, if any) */
633 if (ps->queue_len) {
634 queue_last = ps->queue_begin + ps->queue_len - 1;
635 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
636 queue_last -= NC_PS_QUEUE_SIZE;
637 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200638 *id = ps->queue[queue_last] + 1;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200639 } else {
Michal Vaskob30b99c2016-07-26 11:35:43 +0200640 *id = 0;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200641 }
642
643 /* add ourselves into the queue */
644 if (ps->queue_len == NC_PS_QUEUE_SIZE) {
Michal Vasko26043172016-07-26 14:08:59 +0200645 ERR("%s: pollsession queue too small.", func);
Michal Vasko0ea456b2016-07-26 12:23:24 +0200646 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200647 return -1;
648 }
649 ++ps->queue_len;
650 queue_last = ps->queue_begin + ps->queue_len - 1;
651 if (queue_last > NC_PS_QUEUE_SIZE - 1) {
652 queue_last -= NC_PS_QUEUE_SIZE;
653 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200654 ps->queue[queue_last] = *id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200655
656 /* is it our turn? */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200657 while (ps->queue[ps->queue_begin] != *id) {
Radek Krejci7ac16052016-07-15 11:48:18 +0200658 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200659 ts.tv_sec += NC_READ_TIMEOUT;
660
661 ret = pthread_cond_timedwait(&ps->cond, &ps->lock, &ts);
662 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200663 ERR("%s: failed to wait for a pollsession condition (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200664 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200665 nc_ps_queue_remove_id(ps, *id);
666 pthread_mutex_unlock(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200667 return -1;
668 }
669 }
670
Michal Vaskobe86fe32016-04-07 10:43:03 +0200671 /* UNLOCK */
672 pthread_mutex_unlock(&ps->lock);
673
674 return 0;
675}
676
Michal Vaskof04a52a2016-04-07 10:52:10 +0200677int
Michal Vasko26043172016-07-26 14:08:59 +0200678nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func)
Michal Vaskobe86fe32016-04-07 10:43:03 +0200679{
680 int ret;
681 struct timespec ts;
682
Radek Krejci7ac16052016-07-15 11:48:18 +0200683 nc_gettimespec(&ts);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200684 ts.tv_sec += NC_READ_TIMEOUT;
685
686 /* LOCK */
687 ret = pthread_mutex_timedlock(&ps->lock, &ts);
688 if (ret) {
Michal Vasko26043172016-07-26 14:08:59 +0200689 ERR("%s: failed to lock a pollsession (%s).", func, strerror(ret));
Michal Vaskobe86fe32016-04-07 10:43:03 +0200690 ret = -1;
691 }
692
Michal Vaskob30b99c2016-07-26 11:35:43 +0200693 /* we must be the first, it was our turn after all, right? */
694 if (ps->queue[ps->queue_begin] != id) {
695 ERRINT;
Michal Vaskob1a094b2016-10-05 14:04:52 +0200696 /* UNLOCK */
697 if (!ret) {
698 pthread_mutex_unlock(&ps->lock);
699 }
Michal Vaskob30b99c2016-07-26 11:35:43 +0200700 return -1;
701 }
702
Michal Vaskobe86fe32016-04-07 10:43:03 +0200703 /* remove ourselves from the queue */
Michal Vaskob30b99c2016-07-26 11:35:43 +0200704 nc_ps_queue_remove_id(ps, id);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200705
706 /* broadcast to all other threads that the queue moved */
707 pthread_cond_broadcast(&ps->cond);
708
Michal Vaskobe86fe32016-04-07 10:43:03 +0200709 /* UNLOCK */
710 if (!ret) {
711 pthread_mutex_unlock(&ps->lock);
712 }
713
714 return ret;
715}
716
Michal Vasko428087d2016-01-14 16:04:28 +0100717API struct nc_pollsession *
718nc_ps_new(void)
719{
Michal Vasko48a63ed2016-03-01 09:48:21 +0100720 struct nc_pollsession *ps;
721
722 ps = calloc(1, sizeof(struct nc_pollsession));
Michal Vasko4eb3c312016-03-01 14:09:37 +0100723 if (!ps) {
724 ERRMEM;
725 return NULL;
726 }
Michal Vaskobe86fe32016-04-07 10:43:03 +0200727 pthread_cond_init(&ps->cond, NULL);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100728 pthread_mutex_init(&ps->lock, NULL);
729
730 return ps;
Michal Vasko428087d2016-01-14 16:04:28 +0100731}
732
733API void
734nc_ps_free(struct nc_pollsession *ps)
735{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100736 if (!ps) {
737 return;
738 }
739
Michal Vaskobe86fe32016-04-07 10:43:03 +0200740 if (ps->queue_len) {
741 ERR("FATAL: Freeing a pollsession structure that is currently being worked with!");
742 }
743
Michal Vasko3a715132016-01-21 15:40:31 +0100744 free(ps->pfds);
Michal Vasko428087d2016-01-14 16:04:28 +0100745 free(ps->sessions);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100746 pthread_mutex_destroy(&ps->lock);
Michal Vaskobe86fe32016-04-07 10:43:03 +0200747 pthread_cond_destroy(&ps->cond);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100748
Michal Vasko428087d2016-01-14 16:04:28 +0100749 free(ps);
750}
751
752API int
753nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
754{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200755 uint8_t q_id;
756
Michal Vasko45e53ae2016-04-07 11:46:03 +0200757 if (!ps) {
758 ERRARG("ps");
759 return -1;
760 } else if (!session) {
761 ERRARG("session");
Michal Vasko428087d2016-01-14 16:04:28 +0100762 return -1;
763 }
764
Michal Vasko48a63ed2016-03-01 09:48:21 +0100765 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200766 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200767 return -1;
768 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100769
Michal Vasko428087d2016-01-14 16:04:28 +0100770 ++ps->session_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100771 ps->pfds = nc_realloc(ps->pfds, ps->session_count * sizeof *ps->pfds);
772 ps->sessions = nc_realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
773 if (!ps->pfds || !ps->sessions) {
774 ERRMEM;
775 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200776 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100777 return -1;
778 }
Michal Vasko428087d2016-01-14 16:04:28 +0100779
780 switch (session->ti_type) {
781 case NC_TI_FD:
Michal Vasko3a715132016-01-21 15:40:31 +0100782 ps->pfds[ps->session_count - 1].fd = session->ti.fd.in;
Michal Vasko428087d2016-01-14 16:04:28 +0100783 break;
784
Radek Krejci53691be2016-02-22 13:58:37 +0100785#ifdef NC_ENABLED_SSH
Michal Vasko428087d2016-01-14 16:04:28 +0100786 case NC_TI_LIBSSH:
Michal Vasko3a715132016-01-21 15:40:31 +0100787 ps->pfds[ps->session_count - 1].fd = ssh_get_fd(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100788 break;
789#endif
790
Radek Krejci53691be2016-02-22 13:58:37 +0100791#ifdef NC_ENABLED_TLS
Michal Vasko428087d2016-01-14 16:04:28 +0100792 case NC_TI_OPENSSL:
Michal Vasko3a715132016-01-21 15:40:31 +0100793 ps->pfds[ps->session_count - 1].fd = SSL_get_rfd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100794 break;
795#endif
796
797 default:
798 ERRINT;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100799 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200800 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100801 return -1;
802 }
Michal Vasko3a715132016-01-21 15:40:31 +0100803 ps->pfds[ps->session_count - 1].events = POLLIN;
804 ps->pfds[ps->session_count - 1].revents = 0;
805 ps->sessions[ps->session_count - 1] = session;
Michal Vasko428087d2016-01-14 16:04:28 +0100806
Michal Vasko48a63ed2016-03-01 09:48:21 +0100807 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200808 return nc_ps_unlock(ps, q_id, __func__);
Michal Vasko428087d2016-01-14 16:04:28 +0100809}
810
Michal Vasko48a63ed2016-03-01 09:48:21 +0100811static int
Radek Krejcid5f978f2016-03-03 13:14:45 +0100812_nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session, int index)
Michal Vasko428087d2016-01-14 16:04:28 +0100813{
814 uint16_t i;
815
Radek Krejcid5f978f2016-03-03 13:14:45 +0100816 if (index >= 0) {
817 i = (uint16_t)index;
818 goto remove;
819 }
Michal Vasko428087d2016-01-14 16:04:28 +0100820 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100821 if (ps->sessions[i] == session) {
Radek Krejcid5f978f2016-03-03 13:14:45 +0100822remove:
Michal Vasko428087d2016-01-14 16:04:28 +0100823 --ps->session_count;
Michal Vasko58005732016-02-02 15:50:52 +0100824 if (i < ps->session_count) {
825 ps->sessions[i] = ps->sessions[ps->session_count];
826 memcpy(&ps->pfds[i], &ps->pfds[ps->session_count], sizeof *ps->pfds);
827 } else if (!ps->session_count) {
828 free(ps->sessions);
829 ps->sessions = NULL;
830 free(ps->pfds);
831 ps->pfds = NULL;
832 }
Michal Vasko428087d2016-01-14 16:04:28 +0100833 return 0;
834 }
835 }
836
Michal Vaskof0537d82016-01-29 14:42:38 +0100837 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +0100838}
839
Michal Vasko48a63ed2016-03-01 09:48:21 +0100840API int
841nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
842{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200843 uint8_t q_id;
Michal Vaskobe86fe32016-04-07 10:43:03 +0200844 int ret, ret2;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100845
Michal Vasko45e53ae2016-04-07 11:46:03 +0200846 if (!ps) {
847 ERRARG("ps");
848 return -1;
849 } else if (!session) {
850 ERRARG("session");
Michal Vasko48a63ed2016-03-01 09:48:21 +0100851 return -1;
852 }
853
854 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200855 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200856 return -1;
857 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100858
Radek Krejcid5f978f2016-03-03 13:14:45 +0100859 ret = _nc_ps_del_session(ps, session, -1);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100860
861 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200862 ret2 = nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100863
Michal Vaskobe86fe32016-04-07 10:43:03 +0200864 return (ret || ret2 ? -1 : 0);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100865}
866
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100867API uint16_t
868nc_ps_session_count(struct nc_pollsession *ps)
869{
Michal Vaskob30b99c2016-07-26 11:35:43 +0200870 uint8_t q_id;
Michal Vasko48a63ed2016-03-01 09:48:21 +0100871 uint16_t count;
872
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100873 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200874 ERRARG("ps");
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100875 return 0;
876 }
877
Michal Vasko48a63ed2016-03-01 09:48:21 +0100878 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200879 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +0200880 return -1;
881 }
Michal Vasko48a63ed2016-03-01 09:48:21 +0100882
883 count = ps->session_count;
884
885 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +0200886 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +0100887
888 return count;
Michal Vasko0fdb7ac2016-03-01 09:03:12 +0100889}
890
Michal Vasko71090fc2016-05-24 16:37:28 +0200891/* must be called holding the session lock!
892 * returns: NC_PSPOLL_ERROR,
893 * NC_PSPOLL_BAD_RPC,
894 * NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR,
895 * NC_PSPOLL_RPC
896 */
897static int
Radek Krejci93e80222016-10-03 13:34:25 +0200898nc_server_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
Michal Vasko428087d2016-01-14 16:04:28 +0100899{
900 struct lyxml_elem *xml = NULL;
901 NC_MSG_TYPE msgtype;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200902 struct nc_server_reply *reply = NULL;
Radek Krejcif93c7d42016-04-06 13:41:15 +0200903 int ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100904
Michal Vasko45e53ae2016-04-07 11:46:03 +0200905 if (!session) {
906 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +0200907 return NC_PSPOLL_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +0200908 } else if (!rpc) {
909 ERRARG("rpc");
Michal Vasko71090fc2016-05-24 16:37:28 +0200910 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100911 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100912 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200913 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100914 }
915
916 msgtype = nc_read_msg(session, &xml);
917
918 switch (msgtype) {
919 case NC_MSG_RPC:
Radek Krejcif93c7d42016-04-06 13:41:15 +0200920 *rpc = calloc(1, sizeof **rpc);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100921 if (!*rpc) {
922 ERRMEM;
923 goto error;
924 }
Michal Vaskoca4a2422016-02-02 12:17:14 +0100925
Radek Krejcif93c7d42016-04-06 13:41:15 +0200926 ly_errno = LY_SUCCESS;
Michal Vasko68b3f292016-09-16 12:00:32 +0200927 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child, LYD_OPT_RPC | LYD_OPT_DESTRUCT, NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +0100928 if (!(*rpc)->tree) {
Radek Krejcif93c7d42016-04-06 13:41:15 +0200929 /* parsing RPC failed */
Radek Krejci877e1822016-04-06 16:37:43 +0200930 reply = nc_server_reply_err(nc_err_libyang());
Radek Krejci844662e2016-04-13 16:54:43 +0200931 ret = nc_write_msg(session, NC_MSG_REPLY, xml, reply);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200932 nc_server_reply_free(reply);
933 if (ret == -1) {
934 ERR("Session %u: failed to write reply.", session->id);
Radek Krejcif93c7d42016-04-06 13:41:15 +0200935 }
Michal Vasko71090fc2016-05-24 16:37:28 +0200936 ret = NC_PSPOLL_REPLY_ERROR | NC_PSPOLL_BAD_RPC;
937 } else {
938 ret = NC_PSPOLL_RPC;
Michal Vaskoca4a2422016-02-02 12:17:14 +0100939 }
Michal Vasko428087d2016-01-14 16:04:28 +0100940 (*rpc)->root = xml;
941 break;
942 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +0100943 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200944 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100945 goto error;
946 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +0100947 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200948 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100949 goto error;
950 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +0100951 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko71090fc2016-05-24 16:37:28 +0200952 ret = NC_PSPOLL_BAD_RPC;
Michal Vasko428087d2016-01-14 16:04:28 +0100953 goto error;
954 default:
Michal Vasko71090fc2016-05-24 16:37:28 +0200955 /* NC_MSG_ERROR,
Michal Vasko428087d2016-01-14 16:04:28 +0100956 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
957 */
Michal Vasko71090fc2016-05-24 16:37:28 +0200958 ret = NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100959 break;
960 }
961
Michal Vasko71090fc2016-05-24 16:37:28 +0200962 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100963
964error:
965 /* cleanup */
966 lyxml_free(server_opts.ctx, xml);
967
Michal Vasko71090fc2016-05-24 16:37:28 +0200968 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +0100969}
970
fanchanghu966f2de2016-07-21 02:28:57 -0400971API void
972nc_set_global_rpc_clb(nc_rpc_clb clb)
973{
974 global_rpc_clb = clb;
975}
976
Radek Krejci93e80222016-10-03 13:34:25 +0200977API NC_MSG_TYPE
978nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout)
979{
980 NC_MSG_TYPE result = NC_MSG_NOTIF;
981 int ret;
982
983 /* check parameters */
984 if (!session) {
985 ERRARG("session");
986 return NC_MSG_ERROR;
987 } else if (!notif || !notif->tree || !notif->eventtime) {
988 ERRARG("notif");
989 return NC_MSG_ERROR;
990 }
991
992 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
993 ret = nc_timedlock(session->ti_lock, timeout, __func__);
994 if (ret < 0) {
995 return NC_MSG_ERROR;
996 } else if (!ret) {
997 return NC_MSG_WOULDBLOCK;
998 }
999
1000 ret = nc_write_msg(session, NC_MSG_NOTIF, notif);
1001 if (ret == -1) {
1002 ERR("Session %u: failed to write notification.", session->id);
1003 result = NC_MSG_ERROR;
1004 }
1005 pthread_mutex_unlock(session->ti_lock);
1006
1007 return result;
1008}
1009
Michal Vasko71090fc2016-05-24 16:37:28 +02001010/* must be called holding the session lock!
1011 * returns: NC_PSPOLL_ERROR,
1012 * NC_PSPOLL_ERROR | NC_PSPOLL_REPLY_ERROR,
1013 * NC_PSPOLL_REPLY_ERROR,
1014 * 0
1015 */
1016static int
Radek Krejci93e80222016-10-03 13:34:25 +02001017nc_server_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
Michal Vasko428087d2016-01-14 16:04:28 +01001018{
1019 nc_rpc_clb clb;
1020 struct nc_server_reply *reply;
Michal Vasko90e8e692016-07-13 12:27:57 +02001021 struct lys_node *rpc_act = NULL;
1022 struct lyd_node *next, *elem;
Michal Vasko71090fc2016-05-24 16:37:28 +02001023 int ret = 0, r;
Michal Vasko428087d2016-01-14 16:04:28 +01001024
Michal Vasko4a827e52016-03-03 10:59:00 +01001025 if (!rpc) {
1026 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001027 return NC_PSPOLL_ERROR;
Michal Vasko4a827e52016-03-03 10:59:00 +01001028 }
1029
Michal Vasko90e8e692016-07-13 12:27:57 +02001030 if (rpc->tree->schema->nodetype == LYS_RPC) {
1031 /* RPC */
1032 rpc_act = rpc->tree->schema;
fanchanghu966f2de2016-07-21 02:28:57 -04001033 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001034 /* action */
1035 LY_TREE_DFS_BEGIN(rpc->tree, next, elem) {
1036 if (elem->schema->nodetype == LYS_ACTION) {
1037 rpc_act = elem->schema;
1038 break;
1039 }
1040 LY_TREE_DFS_END(rpc->tree, next, elem);
fanchanghu966f2de2016-07-21 02:28:57 -04001041 }
Michal Vasko90e8e692016-07-13 12:27:57 +02001042 if (!rpc_act) {
1043 ERRINT;
1044 return NC_PSPOLL_ERROR;
1045 }
1046 }
1047
1048 if (!rpc_act->priv) {
1049 /* no callback, reply with a not-implemented error */
Michal Vasko1a38c862016-01-15 15:50:07 +01001050 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +01001051 } else {
Michal Vasko90e8e692016-07-13 12:27:57 +02001052 clb = (nc_rpc_clb)rpc_act->priv;
Michal Vasko428087d2016-01-14 16:04:28 +01001053 reply = clb(rpc->tree, session);
1054 }
1055
1056 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001057 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +01001058 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001059 r = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
1060 if (reply->type == NC_RPL_ERROR) {
1061 ret |= NC_PSPOLL_REPLY_ERROR;
1062 }
1063 nc_server_reply_free(reply);
Michal Vasko428087d2016-01-14 16:04:28 +01001064
Michal Vasko71090fc2016-05-24 16:37:28 +02001065 if (r == -1) {
1066 ERR("Session %u: failed to write reply.", session->id);
1067 ret |= NC_PSPOLL_ERROR;
1068 }
Michal Vasko428087d2016-01-14 16:04:28 +01001069
1070 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
1071 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
1072 session->status = NC_STATUS_INVALID;
1073 }
1074
Michal Vasko71090fc2016-05-24 16:37:28 +02001075 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001076}
1077
1078API int
Michal Vasko71090fc2016-05-24 16:37:28 +02001079nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session)
Michal Vasko428087d2016-01-14 16:04:28 +01001080{
1081 int ret;
Michal Vaskob30b99c2016-07-26 11:35:43 +02001082 uint8_t q_id;
Michal Vasko3512e402016-01-28 16:22:34 +01001083 uint16_t i;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001084 time_t cur_time;
Michal Vasko71090fc2016-05-24 16:37:28 +02001085 struct nc_session *cur_session;
Michal Vasko4a827e52016-03-03 10:59:00 +01001086 struct nc_server_rpc *rpc = NULL;
Michal Vasko428087d2016-01-14 16:04:28 +01001087
1088 if (!ps || !ps->session_count) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001089 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001090 return NC_PSPOLL_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001091 }
1092
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001093 cur_time = time(NULL);
1094
Michal Vasko48a63ed2016-03-01 09:48:21 +01001095 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001096 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001097 return NC_PSPOLL_ERROR;
Michal Vaskobe86fe32016-04-07 10:43:03 +02001098 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001099
Michal Vasko428087d2016-01-14 16:04:28 +01001100 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +01001101 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1102 ERR("Session %u: session not running.", ps->sessions[i]->id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001103 ret = NC_PSPOLL_ERROR;
1104 if (session) {
1105 *session = ps->sessions[i];
1106 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001107 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001108 }
Michal Vaskobd8ef262016-01-20 11:09:27 +01001109
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001110 /* TODO invalidate only sessions without subscription */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001111 if (server_opts.idle_timeout && (cur_time >= ps->sessions[i]->opts.server.last_rpc + server_opts.idle_timeout)) {
Michal Vasko3a715132016-01-21 15:40:31 +01001112 ERR("Session %u: session idle timeout elapsed.", ps->sessions[i]->id);
1113 ps->sessions[i]->status = NC_STATUS_INVALID;
1114 ps->sessions[i]->term_reason = NC_SESSION_TERM_TIMEOUT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001115 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1116 if (session) {
1117 *session = ps->sessions[i];
1118 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001119 goto finish;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +01001120 }
1121
Michal Vasko3a715132016-01-21 15:40:31 +01001122 if (ps->pfds[i].revents) {
Michal Vaskobd8ef262016-01-20 11:09:27 +01001123 break;
1124 }
Michal Vasko428087d2016-01-14 16:04:28 +01001125 }
1126
Michal Vaskobd8ef262016-01-20 11:09:27 +01001127 if (i == ps->session_count) {
Radek Krejci53691be2016-02-22 13:58:37 +01001128#ifdef NC_ENABLED_SSH
Michal Vasko3a715132016-01-21 15:40:31 +01001129retry_poll:
Michal Vasko3512e402016-01-28 16:22:34 +01001130#endif
Michal Vaskobd8ef262016-01-20 11:09:27 +01001131 /* no leftover event */
1132 i = 0;
Michal Vasko3a715132016-01-21 15:40:31 +01001133 ret = poll(ps->pfds, ps->session_count, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001134 if (ret < 0) {
1135 ERR("Poll failed (%s).", strerror(errno));
1136 ret = NC_PSPOLL_ERROR;
1137 goto finish;
1138 } else if (!ret) {
1139 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001140 goto finish;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001141 }
Michal Vasko428087d2016-01-14 16:04:28 +01001142 }
1143
Michal Vaskobd8ef262016-01-20 11:09:27 +01001144 /* find the first fd with POLLIN, we don't care if there are more now */
1145 for (; i < ps->session_count; ++i) {
Michal Vasko46eac552016-05-30 15:27:25 +02001146 if (ps->pfds[i].revents & (POLLHUP | POLLNVAL)) {
Michal Vasko3a715132016-01-21 15:40:31 +01001147 ERR("Session %u: communication socket unexpectedly closed.", ps->sessions[i]->id);
1148 ps->sessions[i]->status = NC_STATUS_INVALID;
1149 ps->sessions[i]->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko71090fc2016-05-24 16:37:28 +02001150 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1151 if (session) {
1152 *session = ps->sessions[i];
1153 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001154 goto finish;
Michal Vasko3a715132016-01-21 15:40:31 +01001155 } else if (ps->pfds[i].revents & POLLERR) {
1156 ERR("Session %u: communication socket error.", ps->sessions[i]->id);
1157 ps->sessions[i]->status = NC_STATUS_INVALID;
1158 ps->sessions[i]->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko71090fc2016-05-24 16:37:28 +02001159 ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
1160 if (session) {
1161 *session = ps->sessions[i];
1162 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001163 goto finish;
Michal Vasko3a715132016-01-21 15:40:31 +01001164 } else if (ps->pfds[i].revents & POLLIN) {
Radek Krejci53691be2016-02-22 13:58:37 +01001165#ifdef NC_ENABLED_SSH
Michal Vasko96164bf2016-01-21 15:41:58 +01001166 if (ps->sessions[i]->ti_type == NC_TI_LIBSSH) {
Michal Vasko3512e402016-01-28 16:22:34 +01001167 uint16_t j;
1168
Michal Vasko96164bf2016-01-21 15:41:58 +01001169 /* things are not that simple with SSH... */
Michal Vasko62be1ce2016-03-03 13:24:52 +01001170 ret = nc_ssh_pollin(ps->sessions[i], timeout);
Michal Vasko96164bf2016-01-21 15:41:58 +01001171
1172 /* clear POLLIN on sessions sharing this session's SSH session */
Michal Vasko71090fc2016-05-24 16:37:28 +02001173 if (ret & (NC_PSPOLL_RPC | NC_PSPOLL_SSH_MSG | NC_PSPOLL_SSH_CHANNEL)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001174 for (j = i + 1; j < ps->session_count; ++j) {
1175 if (ps->pfds[j].fd == ps->pfds[i].fd) {
1176 ps->pfds[j].revents = 0;
1177 }
1178 }
1179 }
1180
Michal Vasko71090fc2016-05-24 16:37:28 +02001181 /* SSH message only */
1182 if (!(ret & (NC_PSPOLL_RPC | NC_PSPOLL_PENDING))) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001183 ps->pfds[i].revents = 0;
Michal Vasko71090fc2016-05-24 16:37:28 +02001184 if (session) {
1185 *session = ps->sessions[i];
1186 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001187 goto finish;
Michal Vasko96164bf2016-01-21 15:41:58 +01001188
1189 /* event occurred on some other channel */
Michal Vasko71090fc2016-05-24 16:37:28 +02001190 } else if (ret & NC_PSPOLL_PENDING) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001191 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001192 if (i == ps->session_count - 1) {
1193 /* last session and it is not the right channel, ... */
Michal Vasko8c748832016-02-03 15:32:16 +01001194 if (!timeout) {
Michal Vasko428087d2016-01-14 16:04:28 +01001195 /* ... timeout is 0, so that is it */
Michal Vasko71090fc2016-05-24 16:37:28 +02001196 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001197 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001198 }
Michal Vasko8c748832016-02-03 15:32:16 +01001199 /* ... retry polling reasonable time apart ... */
1200 usleep(NC_TIMEOUT_STEP);
1201 if (timeout > 0) {
1202 /* ... and decrease timeout, if not -1 */
Michal Vasko7b38e232016-02-26 15:01:07 +01001203 timeout -= NC_TIMEOUT_STEP * 1000;
Michal Vasko8c748832016-02-03 15:32:16 +01001204 }
1205 goto retry_poll;
Michal Vasko428087d2016-01-14 16:04:28 +01001206 }
1207 /* check other sessions */
1208 continue;
Michal Vasko428087d2016-01-14 16:04:28 +01001209 }
1210 }
Radek Krejci53691be2016-02-22 13:58:37 +01001211#endif /* NC_ENABLED_SSH */
Michal Vasko428087d2016-01-14 16:04:28 +01001212
Michal Vaskobd8ef262016-01-20 11:09:27 +01001213 /* we are going to process it now */
Michal Vasko3a715132016-01-21 15:40:31 +01001214 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +01001215 break;
1216 }
1217 }
1218
1219 if (i == ps->session_count) {
1220 ERRINT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001221 ret = NC_PSPOLL_ERROR;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001222 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001223 }
1224
1225 /* this is the session with some data available for reading */
Michal Vasko71090fc2016-05-24 16:37:28 +02001226 cur_session = ps->sessions[i];
1227 if (session) {
1228 *session = cur_session;
1229 }
Michal Vasko428087d2016-01-14 16:04:28 +01001230
Michal Vaskobd8ef262016-01-20 11:09:27 +01001231 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal vasko953939c2016-10-04 13:46:20 +02001232 ret = nc_timedlock(cur_session->ti_lock, timeout, __func__);
Michal Vasko71090fc2016-05-24 16:37:28 +02001233 if (ret < 0) {
1234 ret = NC_PSPOLL_ERROR;
1235 goto finish;
1236 } else if (!ret) {
1237 ret = NC_PSPOLL_TIMEOUT;
Michal Vasko48a63ed2016-03-01 09:48:21 +01001238 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001239 }
1240
Radek Krejci93e80222016-10-03 13:34:25 +02001241 ret = nc_server_recv_rpc(cur_session, &rpc);
Michal Vasko71090fc2016-05-24 16:37:28 +02001242 if (ret & (NC_PSPOLL_ERROR | NC_PSPOLL_BAD_RPC)) {
1243 pthread_mutex_unlock(cur_session->ti_lock);
1244 if (cur_session->status != NC_STATUS_RUNNING) {
1245 ret |= NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko428087d2016-01-14 16:04:28 +01001246 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001247 goto finish;
Michal Vasko428087d2016-01-14 16:04:28 +01001248 }
1249
Michal Vasko2e6defd2016-10-07 15:48:15 +02001250 cur_session->opts.server.last_rpc = time(NULL);
Michal Vaskoca4a2422016-02-02 12:17:14 +01001251
Michal Vasko428087d2016-01-14 16:04:28 +01001252 /* process RPC */
Radek Krejci93e80222016-10-03 13:34:25 +02001253 ret |= nc_server_send_reply(cur_session, rpc);
Michal Vasko428087d2016-01-14 16:04:28 +01001254
Michal Vasko71090fc2016-05-24 16:37:28 +02001255 pthread_mutex_unlock(cur_session->ti_lock);
1256 if (cur_session->status != NC_STATUS_RUNNING) {
1257 ret |= NC_PSPOLL_SESSION_TERM;
1258 if (!(cur_session->term_reason & (NC_SESSION_TERM_CLOSED | NC_SESSION_TERM_KILLED))) {
1259 ret |= NC_PSPOLL_SESSION_ERROR;
1260 }
Michal Vasko428087d2016-01-14 16:04:28 +01001261 }
Radek Krejcif93c7d42016-04-06 13:41:15 +02001262
Michal Vaskoca4a2422016-02-02 12:17:14 +01001263 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vaskobd8ef262016-01-20 11:09:27 +01001264
1265 /* is there some other socket waiting? */
1266 for (++i; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +01001267 if (ps->pfds[i].revents) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001268 ret |= NC_PSPOLL_PENDING;
1269 break;
Michal Vaskobd8ef262016-01-20 11:09:27 +01001270 }
1271 }
1272
Michal Vasko48a63ed2016-03-01 09:48:21 +01001273finish:
1274 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001275 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001276 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +01001277}
1278
Michal Vaskod09eae62016-02-01 10:32:52 +01001279API void
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001280nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *))
Michal Vaskod09eae62016-02-01 10:32:52 +01001281{
Michal Vaskob30b99c2016-07-26 11:35:43 +02001282 uint8_t q_id;
Michal Vaskod09eae62016-02-01 10:32:52 +01001283 uint16_t i;
1284 struct nc_session *session;
1285
Michal Vasko9a25e932016-02-01 10:36:42 +01001286 if (!ps) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001287 ERRARG("ps");
Michal Vasko9a25e932016-02-01 10:36:42 +01001288 return;
1289 }
1290
Michal Vasko48a63ed2016-03-01 09:48:21 +01001291 /* LOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001292 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vaskobe86fe32016-04-07 10:43:03 +02001293 return;
1294 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001295
Michal Vasko48a63ed2016-03-01 09:48:21 +01001296 if (all) {
Radek Krejci4f8042c2016-03-03 13:11:26 +01001297 for (i = 0; i < ps->session_count; i++) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001298 nc_session_free(ps->sessions[i], data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001299 }
1300 free(ps->sessions);
1301 ps->sessions = NULL;
1302 free(ps->pfds);
1303 ps->pfds = NULL;
1304 ps->session_count = 0;
1305 } else {
1306 for (i = 0; i < ps->session_count; ) {
1307 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
1308 session = ps->sessions[i];
Radek Krejcid5f978f2016-03-03 13:14:45 +01001309 _nc_ps_del_session(ps, NULL, i);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001310 nc_session_free(session, data_free);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001311 continue;
1312 }
1313
1314 ++i;
1315 }
Michal Vaskod09eae62016-02-01 10:32:52 +01001316 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001317
1318 /* UNLOCK */
Michal Vasko26043172016-07-26 14:08:59 +02001319 nc_ps_unlock(ps, q_id, __func__);
Michal Vaskod09eae62016-02-01 10:32:52 +01001320}
1321
Radek Krejci53691be2016-02-22 13:58:37 +01001322#if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS)
Michal Vasko9e036d52016-01-08 10:49:26 +01001323
Michal Vaskoe2713da2016-08-22 16:06:40 +02001324API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001325nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +01001326{
Michal Vasko3031aae2016-01-27 16:07:18 +01001327 uint16_t i;
Michal Vasko9e036d52016-01-08 10:49:26 +01001328
Michal Vasko45e53ae2016-04-07 11:46:03 +02001329 if (!name) {
1330 ERRARG("name");
1331 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001332 }
1333
Michal Vasko51e514d2016-02-02 15:51:52 +01001334 /* WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001335 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001336
1337 /* check name uniqueness */
1338 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001339 if (!strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001340 ERR("Endpoint \"%s\" already exists.", name);
Michal Vasko51e514d2016-02-02 15:51:52 +01001341 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001342 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001343 return -1;
1344 }
1345 }
1346
Michal Vasko3031aae2016-01-27 16:07:18 +01001347 ++server_opts.endpt_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +01001348 server_opts.endpts = nc_realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001349 if (!server_opts.endpts) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001350 ERRMEM;
1351 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001352 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001353 return -1;
1354 }
1355 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001356 server_opts.endpts[server_opts.endpt_count - 1].ti = ti;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001357
Michal Vaskoe2713da2016-08-22 16:06:40 +02001358 server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001359 if (!server_opts.binds) {
1360 ERRMEM;
1361 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001362 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001363 return -1;
1364 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001365
Michal Vasko2e6defd2016-10-07 15:48:15 +02001366 server_opts.binds[server_opts.endpt_count - 1].address = NULL;
1367 server_opts.binds[server_opts.endpt_count - 1].port = 0;
1368 server_opts.binds[server_opts.endpt_count - 1].sock = -1;
1369
1370 switch (ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001371#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001372 case NC_TI_LIBSSH:
1373 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1374 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) {
1375 ERRMEM;
1376 /* WRITE UNLOCK */
1377 pthread_rwlock_unlock(&server_opts.endpt_lock);
1378 return -1;
1379 }
1380 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods =
1381 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1382 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3;
1383 server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 10;
1384 break;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001385#endif
Michal Vaskoe2713da2016-08-22 16:06:40 +02001386#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001387 case NC_TI_OPENSSL:
1388 server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1389 if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) {
1390 ERRMEM;
1391 /* WRITE UNLOCK */
1392 pthread_rwlock_unlock(&server_opts.endpt_lock);
1393 return -1;
1394 }
1395 break;
1396#endif
1397 default:
1398 ERRINT;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001399 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001400 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001401 return -1;
1402 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001403
Michal Vasko2e6defd2016-10-07 15:48:15 +02001404 pthread_mutex_init(&server_opts.endpts[server_opts.endpt_count - 1].lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001405
Michal Vasko3031aae2016-01-27 16:07:18 +01001406 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001407 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001408
Michal Vasko9e036d52016-01-08 10:49:26 +01001409 return 0;
1410}
1411
Michal Vasko3031aae2016-01-27 16:07:18 +01001412int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001413nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port)
Michal Vaskoda514772016-02-01 11:32:01 +01001414{
1415 struct nc_endpt *endpt;
1416 struct nc_bind *bind = NULL;
1417 uint16_t i;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001418 int sock = -1, set_addr;
Michal Vaskoda514772016-02-01 11:32:01 +01001419
Michal Vasko45e53ae2016-04-07 11:46:03 +02001420 if (!endpt_name) {
1421 ERRARG("endpt_name");
1422 return -1;
1423 } else if ((!address && !port) || (address && port)) {
1424 ERRARG("address and port");
1425 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001426 }
1427
Michal Vaskoe2713da2016-08-22 16:06:40 +02001428 if (address) {
1429 set_addr = 1;
1430 } else {
1431 set_addr = 0;
1432 }
1433
Michal Vasko51e514d2016-02-02 15:51:52 +01001434 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001435 endpt = nc_server_endpt_lock(endpt_name, 0, &i);
Michal Vaskoda514772016-02-01 11:32:01 +01001436 if (!endpt) {
1437 return -1;
1438 }
1439
Michal Vaskoe2713da2016-08-22 16:06:40 +02001440 bind = &server_opts.binds[i];
Michal Vaskoda514772016-02-01 11:32:01 +01001441
Michal Vaskoe2713da2016-08-22 16:06:40 +02001442 if (set_addr) {
1443 port = bind->port;
Michal Vaskoda514772016-02-01 11:32:01 +01001444 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001445 address = bind->address;
Michal Vaskoda514772016-02-01 11:32:01 +01001446 }
1447
Michal Vaskoe2713da2016-08-22 16:06:40 +02001448 /* we have all the information we need to create a listening socket */
1449 if (address && port) {
1450 /* create new socket, close the old one */
1451 sock = nc_sock_listen(address, port);
1452 if (sock == -1) {
1453 goto fail;
1454 }
1455
1456 if (bind->sock > -1) {
1457 close(bind->sock);
1458 }
1459 bind->sock = sock;
1460 } /* else we are just setting address or port */
1461
1462 if (set_addr) {
Michal Vaskoda514772016-02-01 11:32:01 +01001463 lydict_remove(server_opts.ctx, bind->address);
1464 bind->address = lydict_insert(server_opts.ctx, address, 0);
1465 } else {
1466 bind->port = port;
1467 }
1468
Michal Vaskoe2713da2016-08-22 16:06:40 +02001469 if (sock > -1) {
1470#if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS)
Michal Vasko2e6defd2016-10-07 15:48:15 +02001471 VRB("Listening on %s:%u for %s connections.", address, port, (endpt->ti == NC_TI_LIBSSH ? "SSH" : "TLS"));
Michal Vaskoe2713da2016-08-22 16:06:40 +02001472#elif defined(NC_ENABLED_SSH)
1473 VRB("Listening on %s:%u for SSH connections.", address, port);
1474#else
1475 VRB("Listening on %s:%u for TLS connections.", address, port);
1476#endif
1477 }
1478
Michal Vasko51e514d2016-02-02 15:51:52 +01001479 /* UNLOCK */
Michal Vasko7a93af72016-02-01 16:00:15 +01001480 nc_server_endpt_unlock(endpt);
Michal Vaskoda514772016-02-01 11:32:01 +01001481 return 0;
Michal Vasko51e514d2016-02-02 15:51:52 +01001482
1483fail:
1484 /* UNLOCK */
1485 nc_server_endpt_unlock(endpt);
1486 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +01001487}
1488
Michal Vaskoe2713da2016-08-22 16:06:40 +02001489API int
Michal Vasko2e6defd2016-10-07 15:48:15 +02001490nc_server_endpt_set_address(const char *endpt_name, const char *address)
1491{
1492 return nc_server_endpt_set_address_port(endpt_name, address, 0);
1493}
1494
1495API int
1496nc_server_endpt_set_port(const char *endpt_name, uint16_t port)
1497{
1498 return nc_server_endpt_set_address_port(endpt_name, NULL, port);
1499}
1500
1501API int
Michal Vaskoe2713da2016-08-22 16:06:40 +02001502nc_server_del_endpt(const char *name)
Michal Vasko9e036d52016-01-08 10:49:26 +01001503{
1504 uint32_t i;
1505 int ret = -1;
1506
Michal Vasko3031aae2016-01-27 16:07:18 +01001507 /* WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001508 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001509
Michal Vaskoe2713da2016-08-22 16:06:40 +02001510 if (!name) {
1511 /* remove all endpoints */
Michal Vasko3031aae2016-01-27 16:07:18 +01001512 for (i = 0; i < server_opts.endpt_count; ++i) {
1513 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001514 pthread_mutex_destroy(&server_opts.endpts[i].lock);
1515 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001516#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001517 case NC_TI_LIBSSH:
1518 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1519 free(server_opts.endpts[i].opts.ssh);
1520 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001521#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001522#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001523 case NC_TI_OPENSSL:
1524 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1525 free(server_opts.endpts[i].opts.tls);
1526 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001527#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001528 default:
1529 ERRINT;
1530 /* won't get here ...*/
1531 break;
1532 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001533 ret = 0;
1534 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001535 free(server_opts.endpts);
1536 server_opts.endpts = NULL;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001537
1538 /* remove all binds */
1539 for (i = 0; i < server_opts.endpt_count; ++i) {
1540 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1541 if (server_opts.binds[i].sock > -1) {
1542 close(server_opts.binds[i].sock);
1543 }
1544 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001545 free(server_opts.binds);
1546 server_opts.binds = NULL;
1547
Michal Vasko3031aae2016-01-27 16:07:18 +01001548 server_opts.endpt_count = 0;
1549
Michal Vasko1a38c862016-01-15 15:50:07 +01001550 } else {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001551 /* remove one endpoint with bind(s) */
Michal Vasko3031aae2016-01-27 16:07:18 +01001552 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskoe2713da2016-08-22 16:06:40 +02001553 if (!strcmp(server_opts.endpts[i].name, name)) {
1554 /* remove endpt */
Michal Vasko3031aae2016-01-27 16:07:18 +01001555 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001556 pthread_mutex_destroy(&server_opts.endpts[i].lock);
1557 switch (server_opts.endpts[i].ti) {
Radek Krejci53691be2016-02-22 13:58:37 +01001558#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001559 case NC_TI_LIBSSH:
1560 nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh);
1561 free(server_opts.endpts[i].opts.ssh);
1562 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001563#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001564#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001565 case NC_TI_OPENSSL:
1566 nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls);
1567 free(server_opts.endpts[i].opts.tls);
1568 break;
Michal Vasko3031aae2016-01-27 16:07:18 +01001569#endif
Michal Vasko2e6defd2016-10-07 15:48:15 +02001570 default:
1571 ERRINT;
1572 break;
1573 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001574
Michal Vaskoe2713da2016-08-22 16:06:40 +02001575 /* remove bind(s) */
Michal Vaskoe2713da2016-08-22 16:06:40 +02001576 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1577 if (server_opts.binds[i].sock > -1) {
1578 close(server_opts.binds[i].sock);
1579 }
1580
1581 /* move last endpt and bind(s) to the empty space */
Michal Vasko3031aae2016-01-27 16:07:18 +01001582 --server_opts.endpt_count;
Michal Vaskoc0256492016-02-02 12:19:06 +01001583 if (i < server_opts.endpt_count) {
1584 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1585 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vasko2e6defd2016-10-07 15:48:15 +02001586 } else if (!server_opts.endpt_count) {
Michal Vaskoc0256492016-02-02 12:19:06 +01001587 free(server_opts.binds);
1588 server_opts.binds = NULL;
1589 free(server_opts.endpts);
1590 server_opts.endpts = NULL;
1591 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001592
1593 ret = 0;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001594 break;
Michal Vasko1a38c862016-01-15 15:50:07 +01001595 }
1596 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001597 }
1598
Michal Vasko3031aae2016-01-27 16:07:18 +01001599 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001600 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001601
Michal Vasko9e036d52016-01-08 10:49:26 +01001602 return ret;
1603}
1604
Michal Vasko71090fc2016-05-24 16:37:28 +02001605API NC_MSG_TYPE
Michal Vasko1a38c862016-01-15 15:50:07 +01001606nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001607{
Michal Vasko71090fc2016-05-24 16:37:28 +02001608 NC_MSG_TYPE msgtype;
Michal Vasko1a38c862016-01-15 15:50:07 +01001609 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01001610 char *host = NULL;
Michal Vasko2e6defd2016-10-07 15:48:15 +02001611 uint16_t port, bind_idx;
Michal Vasko9e036d52016-01-08 10:49:26 +01001612
Michal Vasko45e53ae2016-04-07 11:46:03 +02001613 if (!server_opts.ctx) {
1614 ERRINIT;
Michal Vasko71090fc2016-05-24 16:37:28 +02001615 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001616 } else if (!session) {
1617 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001618 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001619 }
1620
Michal Vasko51e514d2016-02-02 15:51:52 +01001621 /* we have to hold WRITE for the whole time, since there is not
1622 * a way of downgrading the lock to READ */
1623 /* WRITE LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001624 pthread_rwlock_wrlock(&server_opts.endpt_lock);
Michal Vasko51e514d2016-02-02 15:51:52 +01001625
1626 if (!server_opts.endpt_count) {
Michal Vasko863a6e92016-07-28 14:27:41 +02001627 ERR("No endpoints to accept sessions on.");
Michal Vasko51e514d2016-02-02 15:51:52 +01001628 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001629 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001630 return NC_MSG_ERROR;
Michal Vasko51e514d2016-02-02 15:51:52 +01001631 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001632
Michal Vaskoe2713da2016-08-22 16:06:40 +02001633 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx);
Michal Vaskob48aa812016-01-18 14:13:09 +01001634
Michal Vasko50456e82016-02-02 12:16:08 +01001635 if (ret < 1) {
Michal Vasko51e514d2016-02-02 15:51:52 +01001636 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001637 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01001638 free(host);
Michal Vasko5e203472016-05-30 15:27:58 +02001639 if (!ret) {
1640 return NC_MSG_WOULDBLOCK;
1641 }
Michal Vasko71090fc2016-05-24 16:37:28 +02001642 return NC_MSG_ERROR;
Michal Vasko9e036d52016-01-08 10:49:26 +01001643 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001644 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001645
Michal Vasko1a38c862016-01-15 15:50:07 +01001646 *session = calloc(1, sizeof **session);
Michal Vasko686aa312016-01-21 15:58:18 +01001647 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001648 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001649 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01001650 free(host);
Michal Vasko71090fc2016-05-24 16:37:28 +02001651 msgtype = NC_MSG_ERROR;
1652 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001653 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001654 (*session)->status = NC_STATUS_STARTING;
1655 (*session)->side = NC_SERVER;
1656 (*session)->ctx = server_opts.ctx;
1657 (*session)->flags = NC_SESSION_SHAREDCTX;
1658 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
1659 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01001660
1661 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +01001662 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1663 if (!(*session)->ti_lock) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001664 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001665 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001666 msgtype = NC_MSG_ERROR;
1667 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001668 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001669 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001670
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001671 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01001672#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02001673 if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) {
1674 (*session)->data = server_opts.endpts[bind_idx].opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01001675 ret = nc_accept_ssh_session(*session, sock, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001676 if (ret < 0) {
1677 msgtype = NC_MSG_ERROR;
1678 goto cleanup;
1679 } else if (!ret) {
1680 msgtype = NC_MSG_WOULDBLOCK;
1681 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001682 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001683 } else
1684#endif
Radek Krejci53691be2016-02-22 13:58:37 +01001685#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02001686 if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) {
1687 (*session)->data = server_opts.endpts[bind_idx].opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01001688 ret = nc_accept_tls_session(*session, sock, timeout);
Michal Vasko71090fc2016-05-24 16:37:28 +02001689 if (ret < 0) {
1690 msgtype = NC_MSG_ERROR;
1691 goto cleanup;
1692 } else if (!ret) {
1693 msgtype = NC_MSG_WOULDBLOCK;
1694 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001695 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001696 } else
1697#endif
1698 {
Michal Vasko9e036d52016-01-08 10:49:26 +01001699 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001700 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02001701 msgtype = NC_MSG_ERROR;
1702 goto cleanup;
Michal Vasko9e036d52016-01-08 10:49:26 +01001703 }
1704
Michal Vasko2cc4c682016-03-01 09:16:48 +01001705 (*session)->data = NULL;
1706
Michal Vasko51e514d2016-02-02 15:51:52 +01001707 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001708 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001709
Michal Vaskob48aa812016-01-18 14:13:09 +01001710 /* assign new SID atomically */
1711 /* LOCK */
1712 pthread_spin_lock(&server_opts.sid_lock);
1713 (*session)->id = server_opts.new_session_id++;
1714 /* UNLOCK */
1715 pthread_spin_unlock(&server_opts.sid_lock);
1716
Michal Vasko9e036d52016-01-08 10:49:26 +01001717 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001718 msgtype = nc_handshake(*session);
1719 if (msgtype != NC_MSG_HELLO) {
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001720 nc_session_free(*session, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001721 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001722 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001723 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02001724 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001725 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01001726
Michal Vasko71090fc2016-05-24 16:37:28 +02001727 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001728
Michal Vasko71090fc2016-05-24 16:37:28 +02001729cleanup:
Michal Vasko3031aae2016-01-27 16:07:18 +01001730 /* WRITE UNLOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +02001731 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001732
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001733 nc_session_free(*session, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001734 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02001735 return msgtype;
Michal Vasko9e036d52016-01-08 10:49:26 +01001736}
1737
Michal Vasko2e6defd2016-10-07 15:48:15 +02001738API int
1739nc_server_ch_add_client(const char *name, NC_TRANSPORT_IMPL ti)
1740{
1741 uint16_t i;
1742
1743 if (!name) {
1744 ERRARG("name");
1745 return -1;
1746 } else if (!ti) {
1747 ERRARG("ti");
1748 return -1;
1749 }
1750
1751 /* WRITE LOCK */
1752 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
1753
1754 /* check name uniqueness */
1755 for (i = 0; i < server_opts.ch_client_count; ++i) {
1756 if (!strcmp(server_opts.ch_clients[i].name, name)) {
1757 ERR("Call Home client \"%s\" already exists.", name);
1758 /* WRITE UNLOCK */
1759 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1760 return -1;
1761 }
1762 }
1763
1764 ++server_opts.ch_client_count;
1765 server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients);
1766 if (!server_opts.ch_clients) {
1767 ERRMEM;
1768 /* WRITE UNLOCK */
1769 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1770 return -1;
1771 }
1772 server_opts.ch_clients[server_opts.ch_client_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
1773 server_opts.ch_clients[server_opts.ch_client_count - 1].ti = ti;
1774
1775 switch (ti) {
1776#ifdef NC_ENABLED_SSH
1777 case NC_TI_LIBSSH:
1778 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts));
1779 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh) {
1780 ERRMEM;
1781 /* WRITE UNLOCK */
1782 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1783 return -1;
1784 }
1785 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_methods =
1786 NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
1787 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_attempts = 3;
1788 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.ssh->auth_timeout = 10;
1789 break;
1790#endif
1791#ifdef NC_ENABLED_TLS
1792 case NC_TI_OPENSSL:
1793 server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
1794 if (!server_opts.ch_clients[server_opts.ch_client_count - 1].opts.tls) {
1795 ERRMEM;
1796 /* WRITE UNLOCK */
1797 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1798 return -1;
1799 }
1800 break;
1801#endif
1802 default:
1803 ERRINT;
1804 /* WRITE UNLOCK */
1805 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1806 return -1;
1807 }
1808
1809 /* set CH default options */
1810 server_opts.ch_clients[server_opts.ch_client_count - 1].start_with = NC_CH_FIRST_LISTED;
1811 server_opts.ch_clients[server_opts.ch_client_count - 1].max_attempts = 3;
1812
1813 pthread_mutex_init(&server_opts.ch_clients[server_opts.ch_client_count - 1].lock, NULL);
1814
1815 /* WRITE UNLOCK */
1816 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1817
1818 return 0;
1819}
1820
1821API int
1822nc_server_ch_del_client(const char *name)
1823{
1824 uint16_t i, j;
1825 int ret = -1;
1826
1827 /* WRITE LOCK */
1828 pthread_rwlock_wrlock(&server_opts.ch_client_lock);
1829
1830 if (!name) {
1831 /* remove all CH clients */
1832 for (i = 0; i < server_opts.ch_client_count; ++i) {
1833 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
1834
1835 /* remove all endpoints */
1836 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
1837 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
1838 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
1839 }
1840 free(server_opts.ch_clients[i].ch_endpts);
1841
1842 switch (server_opts.ch_clients[i].ti) {
1843#ifdef NC_ENABLED_SSH
1844 case NC_TI_LIBSSH:
1845 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
1846 free(server_opts.ch_clients[i].opts.ssh);
1847 break;
1848#endif
1849#ifdef NC_ENABLED_TLS
1850 case NC_TI_OPENSSL:
1851 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
1852 free(server_opts.ch_clients[i].opts.tls);
1853 break;
1854#endif
1855 default:
1856 ERRINT;
1857 /* won't get here ...*/
1858 break;
1859 }
1860
1861 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
1862
1863 ret = 0;
1864 }
1865 free(server_opts.ch_clients);
1866 server_opts.ch_clients = NULL;
1867
1868 server_opts.ch_client_count = 0;
1869
1870 } else {
1871 /* remove one client with endpoint(s) */
1872 for (i = 0; i < server_opts.ch_client_count; ++i) {
1873 if (!strcmp(server_opts.ch_clients[i].name, name)) {
1874 /* remove endpt */
1875 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].name);
1876
1877 switch (server_opts.ch_clients[i].ti) {
1878#ifdef NC_ENABLED_SSH
1879 case NC_TI_LIBSSH:
1880 nc_server_ssh_clear_opts(server_opts.ch_clients[i].opts.ssh);
1881 free(server_opts.ch_clients[i].opts.ssh);
1882 break;
1883#endif
1884#ifdef NC_ENABLED_TLS
1885 case NC_TI_OPENSSL:
1886 nc_server_tls_clear_opts(server_opts.ch_clients[i].opts.tls);
1887 free(server_opts.ch_clients[i].opts.tls);
1888 break;
1889#endif
1890 default:
1891 ERRINT;
1892 break;
1893 }
1894
1895 /* remove all endpoints */
1896 for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; ++j) {
1897 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].name);
1898 lydict_remove(server_opts.ctx, server_opts.ch_clients[i].ch_endpts[j].address);
1899 }
1900 free(server_opts.ch_clients[i].ch_endpts);
1901
1902 pthread_mutex_destroy(&server_opts.ch_clients[i].lock);
1903
1904 /* move last client and endpoint(s) to the empty space */
1905 --server_opts.ch_client_count;
1906 if (i < server_opts.ch_client_count) {
1907 memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count],
1908 sizeof *server_opts.ch_clients);
1909 } else if (!server_opts.ch_client_count) {
1910 free(server_opts.ch_clients);
1911 server_opts.ch_clients = NULL;
1912 }
1913
1914 ret = 0;
1915 break;
1916 }
1917 }
1918 }
1919
1920 /* WRITE UNLOCK */
1921 pthread_rwlock_unlock(&server_opts.ch_client_lock);
1922
1923 return ret;
1924}
1925
1926API int
1927nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name)
1928{
1929 uint16_t i;
1930 struct nc_ch_client *client;
1931
1932 if (!client_name) {
1933 ERRARG("client_name");
1934 return -1;
1935 } else if (!endpt_name) {
1936 ERRARG("endpt_name");
1937 return -1;
1938 }
1939
1940 /* LOCK */
1941 client = nc_server_ch_client_lock(client_name, 0, NULL);
1942 if (!client) {
1943 return -1;
1944 }
1945
1946 for (i = 0; i < client->ch_endpt_count; ++i) {
1947 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
1948 ERR("Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name);
1949 /* UNLOCK */
1950 nc_server_ch_client_unlock(client);
1951 return -1;
1952 }
1953 }
1954
1955 ++client->ch_endpt_count;
1956 client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts);
1957 if (!client->ch_endpts) {
1958 ERRMEM;
1959 /* UNLOCK */
1960 nc_server_ch_client_unlock(client);
1961 return -1;
1962 }
1963
1964 client->ch_endpts[client->ch_endpt_count - 1].name = lydict_insert(server_opts.ctx, endpt_name, 0);
1965 client->ch_endpts[client->ch_endpt_count - 1].address = NULL;
1966 client->ch_endpts[client->ch_endpt_count - 1].port = 0;
1967
1968 /* UNLOCK */
1969 nc_server_ch_client_unlock(client);
1970
1971 return 0;
1972}
1973
1974API int
1975nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name)
1976{
1977 uint16_t i;
1978 int ret = -1;
1979 struct nc_ch_client *client;
1980
1981 if (!client_name) {
1982 ERRARG("client_name");
1983 return -1;
1984 }
1985
1986 /* LOCK */
1987 client = nc_server_ch_client_lock(client_name, 0, NULL);
1988 if (!client) {
1989 return -1;
1990 }
1991
1992 if (!endpt_name) {
1993 /* remove all endpoints */
1994 for (i = 0; i < client->ch_endpt_count; ++i) {
1995 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
1996 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
1997 }
1998 free(client->ch_endpts);
1999 client->ch_endpts = NULL;
2000 client->ch_endpt_count = 0;
2001
2002 ret = 0;
2003 } else {
2004 for (i = 0; i < client->ch_endpt_count; ++i) {
2005 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2006 lydict_remove(server_opts.ctx, client->ch_endpts[i].name);
2007 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2008 }
2009
2010 /* move last endpoint to the empty space */
2011 --client->ch_endpt_count;
2012 if (i < client->ch_endpt_count) {
2013 memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts);
2014 } else if (!server_opts.ch_client_count) {
2015 free(server_opts.ch_clients);
2016 server_opts.ch_clients = NULL;
2017 }
2018
2019 ret = 0;
2020 break;
2021 }
2022 }
2023
2024 /* UNLOCK */
2025 nc_server_ch_client_unlock(client);
2026
2027 return ret;
2028}
2029
2030API int
2031nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address)
2032{
2033 uint16_t i;
2034 int ret = -1;
2035 struct nc_ch_client *client;
2036
2037 if (!client_name) {
2038 ERRARG("client_name");
2039 return -1;
2040 } else if (!endpt_name) {
2041 ERRARG("endpt_name");
2042 return -1;
2043 } else if (!address) {
2044 ERRARG("address");
2045 return -1;
2046 }
2047
2048 /* LOCK */
2049 client = nc_server_ch_client_lock(client_name, 0, NULL);
2050 if (!client) {
2051 return -1;
2052 }
2053
2054 for (i = 0; i < client->ch_endpt_count; ++i) {
2055 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2056 lydict_remove(server_opts.ctx, client->ch_endpts[i].address);
2057 client->ch_endpts[i].address = lydict_insert(server_opts.ctx, address, 0);
2058
2059 ret = 0;
2060 break;
2061 }
2062 }
2063
2064 /* UNLOCK */
2065 nc_server_ch_client_unlock(client);
2066
2067 if (ret == -1) {
2068 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2069 }
2070
2071 return ret;
2072}
2073
2074API int
2075nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port)
2076{
2077 uint16_t i;
2078 int ret = -1;
2079 struct nc_ch_client *client;
2080
2081 if (!client_name) {
2082 ERRARG("client_name");
2083 return -1;
2084 } else if (!endpt_name) {
2085 ERRARG("endpt_name");
2086 return -1;
2087 } else if (!port) {
2088 ERRARG("port");
2089 return -1;
2090 }
2091
2092 /* LOCK */
2093 client = nc_server_ch_client_lock(client_name, 0, NULL);
2094 if (!client) {
2095 return -1;
2096 }
2097
2098 for (i = 0; i < client->ch_endpt_count; ++i) {
2099 if (!strcmp(client->ch_endpts[i].name, endpt_name)) {
2100 client->ch_endpts[i].port = port;
2101
2102 ret = 0;
2103 break;
2104 }
2105 }
2106
2107 /* UNLOCK */
2108 nc_server_ch_client_unlock(client);
2109
2110 if (ret == -1) {
2111 ERR("Call Home client \"%s\" endpoint \"%s\" not found.", client_name, endpt_name);
2112 }
2113
2114 return ret;
2115}
2116
2117API int
2118nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type)
2119{
2120 struct nc_ch_client *client;
2121
2122 if (!client_name) {
2123 ERRARG("client_name");
2124 return -1;
2125 } else if (!conn_type) {
2126 ERRARG("conn_type");
2127 return -1;
2128 }
2129
2130 /* LOCK */
2131 client = nc_server_ch_client_lock(client_name, 0, NULL);
2132 if (!client) {
2133 return -1;
2134 }
2135
2136 if (client->conn_type != conn_type) {
2137 client->conn_type = conn_type;
2138
2139 /* set default options */
2140 switch (conn_type) {
2141 case NC_CH_PERSIST:
2142 client->conn.persist.idle_timeout = 86400;
2143 client->conn.persist.ka_max_wait = 30;
2144 client->conn.persist.ka_max_attempts = 3;
2145 break;
2146 case NC_CH_PERIOD:
2147 client->conn.period.idle_timeout = 300;
2148 client->conn.period.reconnect_timeout = 60;
2149 break;
2150 default:
2151 ERRINT;
2152 break;
2153 }
2154 }
2155
2156 /* UNLOCK */
2157 nc_server_ch_client_unlock(client);
2158
2159 return 0;
2160}
2161
2162API int
2163nc_server_ch_client_persist_set_idle_timeout(const char *client_name, uint32_t idle_timeout)
2164{
2165 struct nc_ch_client *client;
2166
2167 if (!client_name) {
2168 ERRARG("client_name");
2169 return -1;
2170 }
2171
2172 /* LOCK */
2173 client = nc_server_ch_client_lock(client_name, 0, NULL);
2174 if (!client) {
2175 return -1;
2176 }
2177
2178 if (client->conn_type != NC_CH_PERSIST) {
2179 ERR("Call Home client \"%s\" is not of persistent connection type.");
2180 /* UNLOCK */
2181 nc_server_ch_client_unlock(client);
2182 return -1;
2183 }
2184
2185 client->conn.persist.idle_timeout = idle_timeout;
2186
2187 /* UNLOCK */
2188 nc_server_ch_client_unlock(client);
2189
2190 return 0;
2191}
2192
2193API int
2194nc_server_ch_client_persist_set_keep_alive_max_wait(const char *client_name, uint16_t max_wait)
2195{
2196 struct nc_ch_client *client;
2197
2198 if (!client_name) {
2199 ERRARG("client_name");
2200 return -1;
2201 } else if (!max_wait) {
2202 ERRARG("max_wait");
2203 return -1;
2204 }
2205
2206 /* LOCK */
2207 client = nc_server_ch_client_lock(client_name, 0, NULL);
2208 if (!client) {
2209 return -1;
2210 }
2211
2212 if (client->conn_type != NC_CH_PERSIST) {
2213 ERR("Call Home client \"%s\" is not of persistent connection type.");
2214 /* UNLOCK */
2215 nc_server_ch_client_unlock(client);
2216 return -1;
2217 }
2218
2219 client->conn.persist.ka_max_wait = max_wait;
2220
2221 /* UNLOCK */
2222 nc_server_ch_client_unlock(client);
2223
2224 return 0;
2225}
2226
2227API int
2228nc_server_ch_client_persist_set_keep_alive_max_attempts(const char *client_name, uint8_t max_attempts)
2229{
2230 struct nc_ch_client *client;
2231
2232 if (!client_name) {
2233 ERRARG("client_name");
2234 return -1;
2235 }
2236
2237 /* LOCK */
2238 client = nc_server_ch_client_lock(client_name, 0, NULL);
2239 if (!client) {
2240 return -1;
2241 }
2242
2243 if (client->conn_type != NC_CH_PERSIST) {
2244 ERR("Call Home client \"%s\" is not of persistent connection type.");
2245 /* UNLOCK */
2246 nc_server_ch_client_unlock(client);
2247 return -1;
2248 }
2249
2250 client->conn.persist.ka_max_attempts = max_attempts;
2251
2252 /* UNLOCK */
2253 nc_server_ch_client_unlock(client);
2254
2255 return 0;
2256}
2257
2258API int
2259nc_server_ch_client_period_set_idle_timeout(const char *client_name, uint16_t idle_timeout)
2260{
2261 struct nc_ch_client *client;
2262
2263 if (!client_name) {
2264 ERRARG("client_name");
2265 return -1;
2266 }
2267
2268 /* LOCK */
2269 client = nc_server_ch_client_lock(client_name, 0, NULL);
2270 if (!client) {
2271 return -1;
2272 }
2273
2274 if (client->conn_type != NC_CH_PERIOD) {
2275 ERR("Call Home client \"%s\" is not of periodic connection type.");
2276 /* UNLOCK */
2277 nc_server_ch_client_unlock(client);
2278 return -1;
2279 }
2280
2281 client->conn.period.idle_timeout = idle_timeout;
2282
2283 /* UNLOCK */
2284 nc_server_ch_client_unlock(client);
2285
2286 return 0;
2287}
2288
2289API int
2290nc_server_ch_client_period_set_reconnect_timeout(const char *client_name, uint16_t reconnect_timeout)
2291{
2292 struct nc_ch_client *client;
2293
2294 if (!client_name) {
2295 ERRARG("client_name");
2296 return -1;
2297 } else if (!reconnect_timeout) {
2298 ERRARG("reconnect_timeout");
2299 return -1;
2300 }
2301
2302 /* LOCK */
2303 client = nc_server_ch_client_lock(client_name, 0, NULL);
2304 if (!client) {
2305 return -1;
2306 }
2307
2308 if (client->conn_type != NC_CH_PERIOD) {
2309 ERR("Call Home client \"%s\" is not of periodic connection type.");
2310 /* UNLOCK */
2311 nc_server_ch_client_unlock(client);
2312 return -1;
2313 }
2314
2315 client->conn.period.reconnect_timeout = reconnect_timeout;
2316
2317 /* UNLOCK */
2318 nc_server_ch_client_unlock(client);
2319
2320 return 0;
2321}
2322
2323API int
2324nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with)
2325{
2326 struct nc_ch_client *client;
2327
2328 if (!client_name) {
2329 ERRARG("client_name");
2330 return -1;
2331 }
2332
2333 /* LOCK */
2334 client = nc_server_ch_client_lock(client_name, 0, NULL);
2335 if (!client) {
2336 return -1;
2337 }
2338
2339 client->start_with = start_with;
2340
2341 /* UNLOCK */
2342 nc_server_ch_client_unlock(client);
2343
2344 return 0;
2345}
2346
2347API int
2348nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts)
2349{
2350 struct nc_ch_client *client;
2351
2352 if (!client_name) {
2353 ERRARG("client_name");
2354 return -1;
2355 } else if (!max_attempts) {
2356 ERRARG("max_attempts");
2357 return -1;
2358 }
2359
2360 /* LOCK */
2361 client = nc_server_ch_client_lock(client_name, 0, NULL);
2362 if (!client) {
2363 return -1;
2364 }
2365
2366 client->max_attempts = max_attempts;
2367
2368 /* UNLOCK */
2369 nc_server_ch_client_unlock(client);
2370
2371 return 0;
2372}
2373
2374/* client lock is expected to be held */
2375static NC_MSG_TYPE
2376nc_connect_ch_client_endpt(struct nc_ch_client *client, struct nc_ch_endpt *endpt, struct nc_session **session)
Michal Vaskob05053d2016-01-22 16:12:06 +01002377{
Michal Vasko71090fc2016-05-24 16:37:28 +02002378 NC_MSG_TYPE msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002379 int sock, ret;
2380
Michal Vasko2e6defd2016-10-07 15:48:15 +02002381 sock = nc_sock_connect(endpt->address, endpt->port);
Michal Vaskoc61c4492016-01-25 11:13:34 +01002382 if (sock < 0) {
Michal Vasko71090fc2016-05-24 16:37:28 +02002383 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002384 }
2385
2386 *session = calloc(1, sizeof **session);
2387 if (!(*session)) {
2388 ERRMEM;
2389 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002390 return NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002391 }
2392 (*session)->status = NC_STATUS_STARTING;
2393 (*session)->side = NC_SERVER;
2394 (*session)->ctx = server_opts.ctx;
2395 (*session)->flags = NC_SESSION_SHAREDCTX | NC_SESSION_CALLHOME;
Michal Vasko2e6defd2016-10-07 15:48:15 +02002396 (*session)->host = lydict_insert(server_opts.ctx, endpt->address, 0);
2397 (*session)->port = endpt->port;
Michal Vaskob05053d2016-01-22 16:12:06 +01002398
2399 /* transport lock */
2400 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
2401 if (!(*session)->ti_lock) {
2402 ERRMEM;
2403 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002404 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002405 goto fail;
2406 }
2407 pthread_mutex_init((*session)->ti_lock, NULL);
2408
2409 /* sock gets assigned to session or closed */
Radek Krejci53691be2016-02-22 13:58:37 +01002410#ifdef NC_ENABLED_SSH
Michal Vasko2e6defd2016-10-07 15:48:15 +02002411 if (client->ti == NC_TI_LIBSSH) {
2412 (*session)->data = client->opts.ssh;
Michal Vasko0190bc32016-03-02 15:47:49 +01002413 ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002414 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002415
Michal Vasko71090fc2016-05-24 16:37:28 +02002416 if (ret < 0) {
2417 msgtype = NC_MSG_ERROR;
2418 goto fail;
2419 } else if (!ret) {
2420 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002421 goto fail;
2422 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002423 } else
2424#endif
Radek Krejci53691be2016-02-22 13:58:37 +01002425#ifdef NC_ENABLED_TLS
Michal Vasko2e6defd2016-10-07 15:48:15 +02002426 if (client->ti == NC_TI_OPENSSL) {
2427 (*session)->data = client->opts.tls;
Michal Vasko0190bc32016-03-02 15:47:49 +01002428 ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT);
Michal Vasko2cc4c682016-03-01 09:16:48 +01002429 (*session)->data = NULL;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01002430
Michal Vasko71090fc2016-05-24 16:37:28 +02002431 if (ret < 0) {
2432 msgtype = NC_MSG_ERROR;
2433 goto fail;
2434 } else if (!ret) {
2435 msgtype = NC_MSG_WOULDBLOCK;
Michal Vaskob05053d2016-01-22 16:12:06 +01002436 goto fail;
2437 }
Michal Vasko3d865d22016-01-28 16:00:53 +01002438 } else
2439#endif
2440 {
Michal Vaskob05053d2016-01-22 16:12:06 +01002441 ERRINT;
2442 close(sock);
Michal Vasko71090fc2016-05-24 16:37:28 +02002443 msgtype = NC_MSG_ERROR;
Michal Vaskob05053d2016-01-22 16:12:06 +01002444 goto fail;
2445 }
2446
2447 /* assign new SID atomically */
2448 /* LOCK */
2449 pthread_spin_lock(&server_opts.sid_lock);
2450 (*session)->id = server_opts.new_session_id++;
2451 /* UNLOCK */
2452 pthread_spin_unlock(&server_opts.sid_lock);
2453
2454 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02002455 msgtype = nc_handshake(*session);
2456 if (msgtype != NC_MSG_HELLO) {
Michal Vaskob05053d2016-01-22 16:12:06 +01002457 goto fail;
2458 }
Michal Vasko2e6defd2016-10-07 15:48:15 +02002459 (*session)->opts.server.session_start = (*session)->opts.server.last_rpc = time(NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002460 (*session)->status = NC_STATUS_RUNNING;
2461
Michal Vasko71090fc2016-05-24 16:37:28 +02002462 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002463
2464fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01002465 nc_session_free(*session, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +01002466 *session = NULL;
Michal Vasko71090fc2016-05-24 16:37:28 +02002467 return msgtype;
Michal Vaskob05053d2016-01-22 16:12:06 +01002468}
2469
Michal Vasko2e6defd2016-10-07 15:48:15 +02002470/* ms */
2471#define NC_CH_NO_ENDPT_WAIT 1000
2472
2473struct nc_ch_client_thread_arg {
2474 char *client_name;
2475 void (*session_clb)(const char *client_name, struct nc_session *new_session);
2476};
2477
2478static struct nc_ch_client *
2479nc_server_ch_client_with_endpt_lock(const char *name)
2480{
2481 struct nc_ch_client *client;
2482
2483 while (1) {
2484 /* LOCK */
2485 client = nc_server_ch_client_lock(name, 0, NULL);
2486 if (!client) {
2487 return NULL;
2488 }
2489 if (client->ch_endpt_count) {
2490 return client;
2491 }
2492 /* no endpoints defined yet */
2493
2494 /* UNLOCK */
2495 nc_server_ch_client_unlock(client);
2496
2497 usleep(NC_CH_NO_ENDPT_WAIT * 1000);
2498 }
2499
2500 return NULL;
2501}
2502
2503static int
2504nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data)
2505{
2506 int ret;
2507 struct timespec ts;
2508 struct nc_ch_client *client;
2509
2510 /* session created, initialize condition */
2511 session->opts.server.ch_lock = malloc(sizeof *session->opts.server.ch_lock);
2512 session->opts.server.ch_cond = malloc(sizeof *session->opts.server.ch_cond);
2513 if (!session->opts.server.ch_lock || !session->opts.server.ch_cond) {
2514 ERRMEM;
2515 nc_session_free(session, NULL);
2516 return -1;
2517 }
2518 pthread_mutex_init(session->opts.server.ch_lock, NULL);
2519 pthread_cond_init(session->opts.server.ch_cond, NULL);
2520
2521 /* CH LOCK */
2522 pthread_mutex_lock(session->opts.server.ch_lock);
2523
2524 /* give the session to the user */
2525 data->session_clb(data->client_name, session);
2526
2527 do {
2528 nc_gettimespec(&ts);
2529 ts.tv_nsec += NC_CH_NO_ENDPT_WAIT * 1000000L;
2530 if (ts.tv_nsec > 1000000000L) {
2531 ts.tv_sec += ts.tv_nsec / 1000000000L;
2532 ts.tv_nsec %= 1000000000L;
2533 }
2534
2535 ret = pthread_cond_timedwait(session->opts.server.ch_cond, session->opts.server.ch_lock, &ts);
2536 if (ret && (ret != ETIMEDOUT)) {
2537 ERR("Pthread condition timedwait failed (%s).", strerror(ret));
2538 goto ch_client_remove;
2539 }
2540
2541 /* check whether the client was not removed */
2542 /* LOCK */
2543 client = nc_server_ch_client_lock(data->client_name, 0, NULL);
2544 if (!client) {
2545 /* client was removed, finish thread */
2546 VRB("Call Home client \"%s\" removed, but an established session will not be terminated.",
2547 data->client_name);
2548 goto ch_client_remove;
2549 }
2550
2551 /* TODO keep-alives, idle-timeout */
2552
2553 /* UNLOCK */
2554 nc_server_ch_client_unlock(client);
2555
2556 } while (session->status == NC_STATUS_RUNNING);
2557
2558 /* CH UNLOCK */
2559 pthread_mutex_unlock(session->opts.server.ch_lock);
2560
2561 return 0;
2562
2563ch_client_remove:
2564 /* CH UNLOCK */
2565 pthread_mutex_unlock(session->opts.server.ch_lock);
2566
2567 /* TODO make session independent */
2568 return 1;
2569}
2570
2571static void *
2572nc_ch_client_thread(void *arg)
2573{
2574 struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg;
2575 NC_MSG_TYPE msgtype;
2576 uint8_t cur_attempts = 0;
2577 uint16_t i;
2578 char *cur_endpt_name;
2579 struct nc_ch_endpt *cur_endpt;
2580 struct nc_session *session;
2581 struct nc_ch_client *client;
2582
2583 /* LOCK */
2584 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2585 if (!client) {
2586 goto cleanup;
2587 }
2588
2589 cur_endpt = &client->ch_endpts[0];
2590 cur_endpt_name = strdup(cur_endpt->name);
2591
2592 while (1) {
2593 msgtype = nc_connect_ch_client_endpt(client, cur_endpt, &session);
2594
2595 if (msgtype == NC_MSG_HELLO) {
2596 /* UNLOCK */
2597 nc_server_ch_client_unlock(client);
2598
2599 if (nc_server_ch_client_thread_session_cond_wait(session, data)) {
2600 goto cleanup;
2601 }
2602
2603 /* LOCK */
2604 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2605 if (!client) {
2606 goto cleanup;
2607 }
2608
2609 /* session changed status -> it was disconnected for whatever reason,
2610 * persistent connection immediately tries to reconnect, periodic waits some first */
2611 if (client->conn_type == NC_CH_PERIOD) {
2612 i = client->conn.period.reconnect_timeout;
2613
2614 /* UNLOCK */
2615 nc_server_ch_client_unlock(client);
2616
2617 /* TODO wake up sometimes to check for new notifications */
2618 usleep(client->conn.period.reconnect_timeout * 60 * 1000000);
2619
2620 /* LOCK */
2621 client = nc_server_ch_client_with_endpt_lock(data->client_name);
2622 if (!client) {
2623 goto cleanup;
2624 }
2625 }
2626
2627 /* set next endpoint to try */
2628 if (client->start_with == NC_CH_FIRST_LISTED) {
2629 cur_endpt = &client->ch_endpts[0];
2630 free(cur_endpt_name);
2631 cur_endpt_name = strdup(cur_endpt->name);
2632 } /* else we keep the current one */
2633 } else {
2634 /* session was not created */
2635 ++cur_attempts;
2636 if (cur_attempts == client->max_attempts) {
2637 for (i = 0; i < client->ch_endpt_count; ++i) {
2638 if (!strcmp(client->ch_endpts[i].name, cur_endpt_name)) {
2639 break;
2640 }
2641 }
2642 if (i < client->ch_endpt_count - 1) {
2643 /* just go to the next endpoint */
2644 cur_endpt = &client->ch_endpts[i + 1];
2645 free(cur_endpt_name);
2646 cur_endpt_name = strdup(cur_endpt->name);
2647 } else {
2648 /* cur_endpoint was removed or is the last, either way start with the first one */
2649 cur_endpt = &client->ch_endpts[0];
2650 free(cur_endpt_name);
2651 cur_endpt_name = strdup(cur_endpt->name);
2652 }
2653
2654 cur_attempts = 0;
2655 } /* else we keep the current one */
2656 }
2657 }
2658
2659cleanup:
2660 VRB("Call Home client \"%s\" thread exit.", data->client_name);
2661
2662 free(data->client_name);
2663 free(data);
2664 return NULL;
2665}
2666
2667API int
2668nc_connect_ch_client_dispatch(const char *client_name,
2669 void (*session_clb)(const char *client_name, struct nc_session *new_session)) {
2670 int ret;
2671 pthread_t tid;
2672 struct nc_ch_client_thread_arg *arg;
2673
2674 if (!client_name) {
2675 ERRARG("client_name");
2676 return -1;
2677 } else if (!session_clb) {
2678 ERRARG("session_clb");
2679 return -1;
2680 }
2681
2682 arg = malloc(sizeof *arg);
2683 if (!arg) {
2684 ERRMEM;
2685 return -1;
2686 }
2687 arg->client_name = strdup(client_name);
2688 if (!arg->client_name) {
2689 ERRMEM;
2690 free(arg);
2691 return -1;
2692 }
2693 arg->session_clb = session_clb;
2694
2695 ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg);
2696 if (ret) {
2697 ERR("Creating a new thread failed (%s).", strerror(ret));
2698 free(arg->client_name);
2699 free(arg);
2700 return -1;
2701 }
2702 /* the thread now manages arg */
2703
2704 pthread_detach(tid);
2705
2706 return 0;
2707}
2708
Radek Krejci53691be2016-02-22 13:58:37 +01002709#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
Michal Vaskof8352352016-05-24 09:11:36 +02002710
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002711API time_t
2712nc_session_get_start_time(const struct nc_session *session)
Michal Vaskof8352352016-05-24 09:11:36 +02002713{
Michal Vasko2e6defd2016-10-07 15:48:15 +02002714 if (!session || (session->side != NC_SERVER)) {
Michal Vaskof8352352016-05-24 09:11:36 +02002715 ERRARG("session");
Michal Vaskoc45ebd32016-05-25 11:17:36 +02002716 return 0;
Michal Vaskof8352352016-05-24 09:11:36 +02002717 }
2718
Michal Vasko2e6defd2016-10-07 15:48:15 +02002719 return session->opts.server.session_start;
Michal Vaskof8352352016-05-24 09:11:36 +02002720}