blob: 6be7f2d4f4bacf095b98eed32b74bb2f949c030d [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 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of the Company nor the names of its contributors
18 * may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 */
22
23#include <stdint.h>
24#include <stdlib.h>
25#include <errno.h>
26#include <string.h>
27#include <poll.h>
28#include <sys/types.h>
29#include <sys/socket.h>
30#include <netinet/in.h>
31#include <arpa/inet.h>
32#include <unistd.h>
Michal Vaskob48aa812016-01-18 14:13:09 +010033#include <pthread.h>
Michal Vasko11d142a2016-01-19 15:58:24 +010034#include <time.h>
Michal Vasko086311b2016-01-08 09:53:11 +010035
Michal Vasko1a38c862016-01-15 15:50:07 +010036#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010037#include "session_server.h"
38
Michal Vaskob48aa812016-01-18 14:13:09 +010039struct nc_server_opts server_opts = {
Michal Vasko3031aae2016-01-27 16:07:18 +010040 .endpt_array_lock = PTHREAD_RWLOCK_INITIALIZER
Michal Vaskob48aa812016-01-18 14:13:09 +010041};
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010042
Michal Vasko3031aae2016-01-27 16:07:18 +010043extern struct nc_server_ssh_opts ssh_ch_opts;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010044extern pthread_mutex_t ssh_ch_opts_lock;
45
Michal Vasko3031aae2016-01-27 16:07:18 +010046extern struct nc_server_tls_opts tls_ch_opts;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010047extern pthread_mutex_t tls_ch_opts_lock;
Michal Vasko3031aae2016-01-27 16:07:18 +010048
49struct nc_endpt *
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010050nc_server_endpt_lock(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko3031aae2016-01-27 16:07:18 +010051{
52 uint16_t i;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010053 struct nc_endpt *endpt = NULL;
54
55 /* READ LOCK */
56 pthread_rwlock_rdlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010057
58 for (i = 0; i < server_opts.endpt_count; ++i) {
59 if ((server_opts.binds[i].ti == ti) && !strcmp(server_opts.endpts[i].name, name)) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010060 endpt = &server_opts.endpts[i];
61 break;
Michal Vasko3031aae2016-01-27 16:07:18 +010062 }
63 }
64
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010065 if (!endpt) {
66 ERR("Endpoint \"%s\" was not found.", name);
67 /* READ UNLOCK */
68 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
69 return NULL;
70 }
71
72 /* ENDPT LOCK */
73 pthread_mutex_lock(&endpt->endpt_lock);
74
75 return endpt;
76}
77
78void
79nc_server_endpt_unlock(struct nc_endpt *endpt)
80{
81 /* ENDPT UNLOCK */
82 pthread_mutex_unlock(&endpt->endpt_lock);
83
84 /* READ UNLOCK */
Michal Vasko27562ad2016-02-02 15:50:39 +010085 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +010086}
Michal Vasko086311b2016-01-08 09:53:11 +010087
Michal Vasko1a38c862016-01-15 15:50:07 +010088API void
89nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason)
90{
91 if (!session || !reason) {
92 ERRARG;
93 return;
94 }
95
96 session->term_reason = reason;
97}
98
Michal Vasko086311b2016-01-08 09:53:11 +010099int
Michal Vaskof05562c2016-01-20 12:06:43 +0100100nc_sock_listen(const char *address, uint16_t port)
Michal Vasko086311b2016-01-08 09:53:11 +0100101{
102 const int optVal = 1;
103 const socklen_t optLen = sizeof(optVal);
104 int is_ipv4, sock;
105 struct sockaddr_storage saddr;
106
107 struct sockaddr_in *saddr4;
108 struct sockaddr_in6 *saddr6;
109
110
111 if (!strchr(address, ':')) {
112 is_ipv4 = 1;
113 } else {
114 is_ipv4 = 0;
115 }
116
117 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
118 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100119 ERR("Failed to create socket (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100120 goto fail;
121 }
122
123 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&optVal, optLen)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100124 ERR("Could not set socket SO_REUSEADDR socket option (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100125 goto fail;
126 }
127
128 bzero(&saddr, sizeof(struct sockaddr_storage));
129 if (is_ipv4) {
130 saddr4 = (struct sockaddr_in *)&saddr;
131
132 saddr4->sin_family = AF_INET;
133 saddr4->sin_port = htons(port);
134
135 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100136 ERR("Failed to convert IPv4 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100137 goto fail;
138 }
139
140 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100141 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100142 goto fail;
143 }
144
145 } else {
146 saddr6 = (struct sockaddr_in6 *)&saddr;
147
148 saddr6->sin6_family = AF_INET6;
149 saddr6->sin6_port = htons(port);
150
151 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100152 ERR("Failed to convert IPv6 address \"%s\".", address);
Michal Vasko086311b2016-01-08 09:53:11 +0100153 goto fail;
154 }
155
156 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100157 ERR("Could not bind \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100158 goto fail;
159 }
160 }
161
Michal Vaskofb89d772016-01-08 12:25:35 +0100162 if (listen(sock, NC_REVERSE_QUEUE) == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100163 ERR("Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100164 goto fail;
165 }
166
167 return sock;
168
169fail:
170 if (sock > -1) {
171 close(sock);
172 }
173
174 return -1;
175}
176
177int
Michal Vasko3031aae2016-01-27 16:07:18 +0100178nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, int timeout, char **host, uint16_t *port, uint16_t *idx)
Michal Vasko086311b2016-01-08 09:53:11 +0100179{
180 uint16_t i;
181 struct pollfd *pfd;
182 struct sockaddr_storage saddr;
183 socklen_t saddr_len = sizeof(saddr);
184 int ret, sock = -1;
185
186 pfd = malloc(bind_count * sizeof *pfd);
187 for (i = 0; i < bind_count; ++i) {
188 pfd[i].fd = binds[i].sock;
189 pfd[i].events = POLLIN;
190 pfd[i].revents = 0;
191 }
192
193 /* poll for a new connection */
194 errno = 0;
195 ret = poll(pfd, bind_count, timeout);
196 if (!ret) {
197 /* we timeouted */
198 free(pfd);
199 return 0;
200 } else if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100201 ERR("Poll failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100202 free(pfd);
203 return -1;
204 }
205
206 for (i = 0; i < bind_count; ++i) {
207 if (pfd[i].revents & POLLIN) {
208 sock = pfd[i].fd;
209 break;
210 }
211 }
212 free(pfd);
213
214 if (sock == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100215 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100216 return -1;
217 }
218
219 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
Michal Vasko3f6cc4a2016-01-21 15:58:53 +0100220 if (ret < 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100221 ERR("Accept failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100222 return -1;
223 }
224
Michal Vasko3031aae2016-01-27 16:07:18 +0100225 if (idx) {
226 *idx = i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100227 }
228
Michal Vasko086311b2016-01-08 09:53:11 +0100229 /* host was requested */
230 if (host) {
231 if (saddr.ss_family == AF_INET) {
232 *host = malloc(15);
233 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, 15)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100234 ERR("inet_ntop failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100235 free(*host);
236 *host = NULL;
237 }
238
239 if (port) {
240 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
241 }
242 } else if (saddr.ss_family == AF_INET6) {
243 *host = malloc(40);
244 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, 40)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100245 ERR("inet_ntop failed (%s).", strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100246 free(*host);
247 *host = NULL;
248 }
249
250 if (port) {
251 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
252 }
253 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100254 ERR("Source host of an unknown protocol family.");
Michal Vasko086311b2016-01-08 09:53:11 +0100255 }
256 }
257
258 return ret;
259}
260
Michal Vasko05ba9df2016-01-13 14:40:27 +0100261static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100262nc_clb_default_get_schema(struct lyd_node *rpc, struct nc_session *UNUSED(session))
Michal Vasko05ba9df2016-01-13 14:40:27 +0100263{
264 const char *identifier = NULL, *version = NULL, *format = NULL;
265 char *model_data = NULL;
266 const struct lys_module *module;
267 struct nc_server_error *err;
268 struct lyd_node *child, *data = NULL;
Michal Vasko11d142a2016-01-19 15:58:24 +0100269 const struct lys_node *sdata = NULL;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100270
271 LY_TREE_FOR(rpc->child, child) {
272 if (!strcmp(child->schema->name, "identifier")) {
273 identifier = ((struct lyd_node_leaf_list *)child)->value_str;
274 } else if (!strcmp(child->schema->name, "version")) {
275 version = ((struct lyd_node_leaf_list *)child)->value_str;
276 } else if (!strcmp(child->schema->name, "format")) {
277 format = ((struct lyd_node_leaf_list *)child)->value_str;
278 }
279 }
280
281 /* check version */
282 if (version && (strlen(version) != 10) && strcmp(version, "1.0")) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100283 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
284 nc_err_set_msg(err, "The requested version is not supported.", "en");
285 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100286 }
287
288 /* check and get module with the name identifier */
289 module = ly_ctx_get_module(server_opts.ctx, identifier, version);
290 if (!module) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100291 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
292 nc_err_set_msg(err, "The requested schema was not found.", "en");
293 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100294 }
295
296 /* check format */
297 if (!format || !strcmp(format, "yang")) {
298 lys_print_mem(&model_data, module, LYS_OUT_YANG, NULL);
299 } else if (!strcmp(format, "yin")) {
300 lys_print_mem(&model_data, module, LYS_OUT_YIN, NULL);
301 } else {
Michal Vasko1a38c862016-01-15 15:50:07 +0100302 err = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_APP);
303 nc_err_set_msg(err, "The requested format is not supported.", "en");
304 return nc_server_reply_err(err);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100305 }
306
Michal Vaskofea54dc2016-02-17 13:12:16 +0100307 sdata = ly_ctx_get_node(server_opts.ctx, "/ietf-netconf-monitoring:get-schema/output/data");
Michal Vasko0473c4c2016-01-19 10:40:06 +0100308 if (model_data && sdata) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100309 nc_ctx_lock(-1, NULL);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100310 data = lyd_output_new_anyxml(sdata, model_data);
Michal Vasko11d142a2016-01-19 15:58:24 +0100311 nc_ctx_unlock();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100312 }
313 free(model_data);
314 if (!data) {
315 ERRINT;
316 return NULL;
317 }
318
319 return nc_server_reply_data(data, NC_PARAMTYPE_FREE);
320}
321
322static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100323nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100324{
Michal Vasko428087d2016-01-14 16:04:28 +0100325 session->term_reason = NC_SESSION_TERM_CLOSED;
326 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100327}
328
Michal Vasko086311b2016-01-08 09:53:11 +0100329API int
330nc_server_init(struct ly_ctx *ctx)
331{
Michal Vasko05ba9df2016-01-13 14:40:27 +0100332 const struct lys_node *rpc;
333
Michal Vasko086311b2016-01-08 09:53:11 +0100334 if (!ctx) {
335 ERRARG;
336 return -1;
337 }
338
Michal Vasko05ba9df2016-01-13 14:40:27 +0100339 /* set default <get-schema> callback if not specified */
Michal Vaskofea54dc2016-02-17 13:12:16 +0100340 rpc = ly_ctx_get_node(ctx, "/ietf-netconf-monitoring:get-schema");
Michal Vasko05ba9df2016-01-13 14:40:27 +0100341 if (rpc && !rpc->private) {
342 lys_set_private(rpc, nc_clb_default_get_schema);
343 }
344
345 /* set default <close-session> callback if not specififed */
Michal Vaskofea54dc2016-02-17 13:12:16 +0100346 rpc = ly_ctx_get_node(ctx, "/ietf-netconf:close-session");
Michal Vasko05ba9df2016-01-13 14:40:27 +0100347 if (rpc && !rpc->private) {
348 lys_set_private(rpc, nc_clb_default_close_session);
349 }
350
Michal Vasko086311b2016-01-08 09:53:11 +0100351 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100352
353 server_opts.new_session_id = 1;
354 pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
355
Michal Vasko086311b2016-01-08 09:53:11 +0100356 return 0;
357}
358
Michal Vaskob48aa812016-01-18 14:13:09 +0100359API void
360nc_server_destroy(void)
361{
362 pthread_spin_destroy(&server_opts.sid_lock);
363
364#if defined(ENABLE_SSH) || defined(ENABLE_TLS)
Michal Vasko3031aae2016-01-27 16:07:18 +0100365 nc_server_del_endpt(NULL, 0);
Michal Vaskob48aa812016-01-18 14:13:09 +0100366#endif
367}
368
Michal Vasko086311b2016-01-08 09:53:11 +0100369API int
370nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
371{
372 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)
373 || (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT)))) {
374 ERRARG;
375 return -1;
376 }
377
378 server_opts.wd_basic_mode = basic_mode;
379 server_opts.wd_also_supported = also_supported;
380 return 0;
381}
382
Michal Vasko1a38c862016-01-15 15:50:07 +0100383API void
Michal Vasko086311b2016-01-08 09:53:11 +0100384nc_server_set_capab_interleave(int interleave_support)
385{
386 if (interleave_support) {
387 server_opts.interleave_capab = 1;
388 } else {
389 server_opts.interleave_capab = 0;
390 }
Michal Vasko086311b2016-01-08 09:53:11 +0100391}
392
Michal Vasko1a38c862016-01-15 15:50:07 +0100393API void
Michal Vasko086311b2016-01-08 09:53:11 +0100394nc_server_set_hello_timeout(uint16_t hello_timeout)
395{
Michal Vasko086311b2016-01-08 09:53:11 +0100396 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100397}
398
Michal Vasko1a38c862016-01-15 15:50:07 +0100399API void
Michal Vasko086311b2016-01-08 09:53:11 +0100400nc_server_set_idle_timeout(uint16_t idle_timeout)
401{
Michal Vasko086311b2016-01-08 09:53:11 +0100402 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100403}
404
405API int
Michal Vasko1a38c862016-01-15 15:50:07 +0100406nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100407{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100408 if (!server_opts.ctx || (fdin < 0) || (fdout < 0) || !username || !session) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100409 ERRARG;
410 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100411 }
412
413 /* prepare session structure */
Michal Vasko1a38c862016-01-15 15:50:07 +0100414 *session = calloc(1, sizeof **session);
415 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100416 ERRMEM;
Michal Vasko1a38c862016-01-15 15:50:07 +0100417 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100418 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100419 (*session)->status = NC_STATUS_STARTING;
420 (*session)->side = NC_SERVER;
Michal Vasko086311b2016-01-08 09:53:11 +0100421
422 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100423 (*session)->ti_type = NC_TI_FD;
424 (*session)->ti.fd.in = fdin;
425 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100426
427 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100428 (*session)->flags = NC_SESSION_SHAREDCTX;
429 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100430
Michal Vaskob48aa812016-01-18 14:13:09 +0100431 /* assign new SID atomically */
432 pthread_spin_lock(&server_opts.sid_lock);
433 (*session)->id = server_opts.new_session_id++;
434 pthread_spin_unlock(&server_opts.sid_lock);
435
Michal Vasko086311b2016-01-08 09:53:11 +0100436 /* NETCONF handshake */
Michal Vasko1a38c862016-01-15 15:50:07 +0100437 if (nc_handshake(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100438 goto fail;
439 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100440 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100441 (*session)->last_rpc = time(NULL);
Michal Vasko086311b2016-01-08 09:53:11 +0100442
Michal Vasko1a38c862016-01-15 15:50:07 +0100443 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100444
445fail:
Michal Vasko1a38c862016-01-15 15:50:07 +0100446 nc_session_free(*session);
447 *session = NULL;
448 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100449}
Michal Vasko9e036d52016-01-08 10:49:26 +0100450
Michal Vasko428087d2016-01-14 16:04:28 +0100451API struct nc_pollsession *
452nc_ps_new(void)
453{
454 return calloc(1, sizeof(struct nc_pollsession));
455}
456
457API void
458nc_ps_free(struct nc_pollsession *ps)
459{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100460 if (!ps) {
461 return;
462 }
463
Michal Vasko3a715132016-01-21 15:40:31 +0100464 free(ps->pfds);
Michal Vasko428087d2016-01-14 16:04:28 +0100465 free(ps->sessions);
466 free(ps);
467}
468
469API int
470nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
471{
472 if (!ps || !session) {
473 ERRARG;
474 return -1;
475 }
476
477 ++ps->session_count;
Michal Vasko3a715132016-01-21 15:40:31 +0100478 ps->pfds = realloc(ps->pfds, ps->session_count * sizeof *ps->pfds);
Michal Vasko428087d2016-01-14 16:04:28 +0100479 ps->sessions = realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
480
481 switch (session->ti_type) {
482 case NC_TI_FD:
Michal Vasko3a715132016-01-21 15:40:31 +0100483 ps->pfds[ps->session_count - 1].fd = session->ti.fd.in;
Michal Vasko428087d2016-01-14 16:04:28 +0100484 break;
485
486#ifdef ENABLE_SSH
487 case NC_TI_LIBSSH:
Michal Vasko3a715132016-01-21 15:40:31 +0100488 ps->pfds[ps->session_count - 1].fd = ssh_get_fd(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100489 break;
490#endif
491
492#ifdef ENABLE_TLS
493 case NC_TI_OPENSSL:
Michal Vasko3a715132016-01-21 15:40:31 +0100494 ps->pfds[ps->session_count - 1].fd = SSL_get_rfd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100495 break;
496#endif
497
498 default:
499 ERRINT;
500 return -1;
501 }
Michal Vasko3a715132016-01-21 15:40:31 +0100502 ps->pfds[ps->session_count - 1].events = POLLIN;
503 ps->pfds[ps->session_count - 1].revents = 0;
504 ps->sessions[ps->session_count - 1] = session;
Michal Vasko428087d2016-01-14 16:04:28 +0100505
506 return 0;
507}
508
509API int
510nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
511{
512 uint16_t i;
513
514 if (!ps || !session) {
515 ERRARG;
516 return -1;
517 }
518
519 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100520 if (ps->sessions[i] == session) {
Michal Vasko428087d2016-01-14 16:04:28 +0100521 --ps->session_count;
Michal Vasko58005732016-02-02 15:50:52 +0100522 if (i < ps->session_count) {
523 ps->sessions[i] = ps->sessions[ps->session_count];
524 memcpy(&ps->pfds[i], &ps->pfds[ps->session_count], sizeof *ps->pfds);
525 } else if (!ps->session_count) {
526 free(ps->sessions);
527 ps->sessions = NULL;
528 free(ps->pfds);
529 ps->pfds = NULL;
530 }
Michal Vasko428087d2016-01-14 16:04:28 +0100531 return 0;
532 }
533 }
534
Michal Vaskof0537d82016-01-29 14:42:38 +0100535 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +0100536}
537
538/* must be called holding the session lock! */
539static NC_MSG_TYPE
540nc_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
541{
542 struct lyxml_elem *xml = NULL;
543 NC_MSG_TYPE msgtype;
544
545 if (!session || !rpc) {
546 ERRARG;
547 return NC_MSG_ERROR;
548 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100549 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100550 return NC_MSG_ERROR;
551 }
552
553 msgtype = nc_read_msg(session, &xml);
554
555 switch (msgtype) {
556 case NC_MSG_RPC:
557 *rpc = malloc(sizeof **rpc);
Michal Vaskoca4a2422016-02-02 12:17:14 +0100558
Michal Vasko11d142a2016-01-19 15:58:24 +0100559 nc_ctx_lock(-1, NULL);
Michal Vasko428087d2016-01-14 16:04:28 +0100560 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child, LYD_OPT_DESTRUCT | LYD_OPT_RPC);
Michal Vasko51e514d2016-02-02 15:51:52 +0100561 nc_ctx_unlock();
562
Michal Vaskoca4a2422016-02-02 12:17:14 +0100563 if (!(*rpc)->tree) {
564 ERR("Session %u: received message failed to be parsed into a known RPC.", session->id);
565 msgtype = NC_MSG_NONE;
566 }
Michal Vasko428087d2016-01-14 16:04:28 +0100567 (*rpc)->root = xml;
568 break;
569 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +0100570 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100571 goto error;
572 case NC_MSG_REPLY:
Michal Vasko81614ee2016-02-02 12:20:14 +0100573 ERR("Session %u: received <rpc-reply> from a NETCONF client.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100574 goto error;
575 case NC_MSG_NOTIF:
Michal Vasko81614ee2016-02-02 12:20:14 +0100576 ERR("Session %u: received <notification> from a NETCONF client.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100577 goto error;
578 default:
579 /* NC_MSG_ERROR - pass it out;
580 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
581 */
582 break;
583 }
584
585 return msgtype;
586
587error:
588 /* cleanup */
589 lyxml_free(server_opts.ctx, xml);
590
591 return NC_MSG_ERROR;
592}
593
594/* must be called holding the session lock! */
595static NC_MSG_TYPE
596nc_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
597{
598 nc_rpc_clb clb;
599 struct nc_server_reply *reply;
600 int ret;
601
602 /* no callback, reply with a not-implemented error */
Michal Vaskoca4a2422016-02-02 12:17:14 +0100603 if (!rpc->tree || !rpc->tree->schema->private) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100604 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +0100605 } else {
606 clb = (nc_rpc_clb)rpc->tree->schema->private;
607 reply = clb(rpc->tree, session);
608 }
609
610 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100611 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +0100612 }
613
614 ret = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
615
616 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
617 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
618 session->status = NC_STATUS_INVALID;
619 }
620
621 if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100622 ERR("Session %u: failed to write reply.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100623 nc_server_reply_free(reply);
624 return NC_MSG_ERROR;
625 }
626 nc_server_reply_free(reply);
627
628 return NC_MSG_REPLY;
629}
630
631API int
632nc_ps_poll(struct nc_pollsession *ps, int timeout)
633{
634 int ret;
Michal Vasko3512e402016-01-28 16:22:34 +0100635 uint16_t i;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100636 time_t cur_time;
Michal Vasko428087d2016-01-14 16:04:28 +0100637 NC_MSG_TYPE msgtype;
638 struct nc_session *session;
639 struct nc_server_rpc *rpc;
Michal Vasko96164bf2016-01-21 15:41:58 +0100640 struct timespec old_ts;
Michal Vasko428087d2016-01-14 16:04:28 +0100641
642 if (!ps || !ps->session_count) {
643 ERRARG;
644 return -1;
645 }
646
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100647 cur_time = time(NULL);
648
Michal Vasko428087d2016-01-14 16:04:28 +0100649 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100650 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
651 ERR("Session %u: session not running.", ps->sessions[i]->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100652 return -1;
653 }
Michal Vaskobd8ef262016-01-20 11:09:27 +0100654
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100655 /* TODO invalidate only sessions without subscription */
Michal Vasko3a715132016-01-21 15:40:31 +0100656 if (server_opts.idle_timeout && (ps->sessions[i]->last_rpc + server_opts.idle_timeout >= cur_time)) {
657 ERR("Session %u: session idle timeout elapsed.", ps->sessions[i]->id);
658 ps->sessions[i]->status = NC_STATUS_INVALID;
659 ps->sessions[i]->term_reason = NC_SESSION_TERM_TIMEOUT;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100660 return 3;
661 }
662
Michal Vasko3a715132016-01-21 15:40:31 +0100663 if (ps->pfds[i].revents) {
Michal Vaskobd8ef262016-01-20 11:09:27 +0100664 break;
665 }
Michal Vasko428087d2016-01-14 16:04:28 +0100666 }
667
668 if (timeout > 0) {
669 clock_gettime(CLOCK_MONOTONIC_RAW, &old_ts);
670 }
671
Michal Vaskobd8ef262016-01-20 11:09:27 +0100672 if (i == ps->session_count) {
Michal Vasko3512e402016-01-28 16:22:34 +0100673#ifdef ENABLE_SSH
Michal Vasko3a715132016-01-21 15:40:31 +0100674retry_poll:
Michal Vasko3512e402016-01-28 16:22:34 +0100675#endif
Michal Vaskobd8ef262016-01-20 11:09:27 +0100676 /* no leftover event */
677 i = 0;
Michal Vasko3a715132016-01-21 15:40:31 +0100678 ret = poll(ps->pfds, ps->session_count, timeout);
Michal Vaskobd8ef262016-01-20 11:09:27 +0100679 if (ret < 1) {
680 return ret;
681 }
Michal Vasko428087d2016-01-14 16:04:28 +0100682 }
683
Michal Vaskobd8ef262016-01-20 11:09:27 +0100684 /* find the first fd with POLLIN, we don't care if there are more now */
685 for (; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100686 if (ps->pfds[i].revents & POLLHUP) {
687 ERR("Session %u: communication socket unexpectedly closed.", ps->sessions[i]->id);
688 ps->sessions[i]->status = NC_STATUS_INVALID;
689 ps->sessions[i]->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vaskobd8ef262016-01-20 11:09:27 +0100690 return 3;
Michal Vasko3a715132016-01-21 15:40:31 +0100691 } else if (ps->pfds[i].revents & POLLERR) {
692 ERR("Session %u: communication socket error.", ps->sessions[i]->id);
693 ps->sessions[i]->status = NC_STATUS_INVALID;
694 ps->sessions[i]->term_reason = NC_SESSION_TERM_OTHER;
Michal Vaskobd8ef262016-01-20 11:09:27 +0100695 return 3;
Michal Vasko3a715132016-01-21 15:40:31 +0100696 } else if (ps->pfds[i].revents & POLLIN) {
Michal Vasko428087d2016-01-14 16:04:28 +0100697#ifdef ENABLE_SSH
Michal Vasko96164bf2016-01-21 15:41:58 +0100698 if (ps->sessions[i]->ti_type == NC_TI_LIBSSH) {
Michal Vasko3512e402016-01-28 16:22:34 +0100699 uint16_t j;
700
Michal Vasko96164bf2016-01-21 15:41:58 +0100701 /* things are not that simple with SSH... */
702 ret = nc_ssh_pollin(ps->sessions[i], &timeout);
703
704 /* clear POLLIN on sessions sharing this session's SSH session */
705 if ((ret == 1) || (ret >= 4)) {
706 for (j = i + 1; j < ps->session_count; ++j) {
707 if (ps->pfds[j].fd == ps->pfds[i].fd) {
708 ps->pfds[j].revents = 0;
709 }
710 }
711 }
712
713 /* actual event happened */
714 if ((ret <= 0) || (ret >= 3)) {
715 ps->pfds[i].revents = 0;
716 return ret;
717
718 /* event occurred on some other channel */
719 } else if (ret == 2) {
720 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100721 if (i == ps->session_count - 1) {
722 /* last session and it is not the right channel, ... */
Michal Vasko8c748832016-02-03 15:32:16 +0100723 if (!timeout) {
Michal Vasko428087d2016-01-14 16:04:28 +0100724 /* ... timeout is 0, so that is it */
725 return 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100726 }
Michal Vasko8c748832016-02-03 15:32:16 +0100727 /* ... retry polling reasonable time apart ... */
728 usleep(NC_TIMEOUT_STEP);
729 if (timeout > 0) {
730 /* ... and decrease timeout, if not -1 */
731 nc_subtract_elapsed(&timeout, &old_ts);
732 }
733 goto retry_poll;
Michal Vasko428087d2016-01-14 16:04:28 +0100734 }
735 /* check other sessions */
736 continue;
Michal Vasko428087d2016-01-14 16:04:28 +0100737 }
738 }
739#endif /* ENABLE_SSH */
740
Michal Vaskobd8ef262016-01-20 11:09:27 +0100741 /* we are going to process it now */
Michal Vasko3a715132016-01-21 15:40:31 +0100742 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100743 break;
744 }
745 }
746
747 if (i == ps->session_count) {
748 ERRINT;
749 return -1;
750 }
751
752 /* this is the session with some data available for reading */
Michal Vasko3a715132016-01-21 15:40:31 +0100753 session = ps->sessions[i];
Michal Vasko428087d2016-01-14 16:04:28 +0100754
755 if (timeout > 0) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100756 nc_subtract_elapsed(&timeout, &old_ts);
Michal Vasko428087d2016-01-14 16:04:28 +0100757 }
758
Michal Vaskobd8ef262016-01-20 11:09:27 +0100759 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100760 ret = nc_timedlock(session->ti_lock, timeout, NULL);
761 if (ret != 1) {
762 /* error or timeout */
763 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100764 }
765
766 msgtype = nc_recv_rpc(session, &rpc);
767 if (msgtype == NC_MSG_ERROR) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100768 pthread_mutex_unlock(session->ti_lock);
Michal Vasko428087d2016-01-14 16:04:28 +0100769 if (session->status != NC_STATUS_RUNNING) {
Michal Vaskobd8ef262016-01-20 11:09:27 +0100770 return 3;
Michal Vasko428087d2016-01-14 16:04:28 +0100771 }
772 return -1;
773 }
774
Michal Vaskoca4a2422016-02-02 12:17:14 +0100775 /* NC_MSG_NONE is not a real (known) RPC */
776 if (msgtype == NC_MSG_RPC) {
777 session->last_rpc = time(NULL);
778 }
779
Michal Vasko428087d2016-01-14 16:04:28 +0100780 /* process RPC */
781 msgtype = nc_send_reply(session, rpc);
782
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100783 pthread_mutex_unlock(session->ti_lock);
Michal Vasko428087d2016-01-14 16:04:28 +0100784
785 if (msgtype == NC_MSG_ERROR) {
Michal Vaskoca4a2422016-02-02 12:17:14 +0100786 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vasko428087d2016-01-14 16:04:28 +0100787 return -1;
788 }
Michal Vaskoca4a2422016-02-02 12:17:14 +0100789 nc_server_rpc_free(rpc, server_opts.ctx);
Michal Vaskobd8ef262016-01-20 11:09:27 +0100790
Michal Vaskobd8b4e12016-01-22 16:11:20 +0100791 /* status change takes precedence over leftover events (return 2) */
792 if (session->status != NC_STATUS_RUNNING) {
793 return 3;
794 }
795
Michal Vaskobd8ef262016-01-20 11:09:27 +0100796 /* is there some other socket waiting? */
797 for (++i; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100798 if (ps->pfds[i].revents) {
Michal Vaskobd8ef262016-01-20 11:09:27 +0100799 return 2;
800 }
801 }
802
Michal Vasko428087d2016-01-14 16:04:28 +0100803 return 1;
804}
805
Michal Vaskod09eae62016-02-01 10:32:52 +0100806API void
807nc_ps_clear(struct nc_pollsession *ps)
808{
809 uint16_t i;
810 struct nc_session *session;
811
Michal Vasko9a25e932016-02-01 10:36:42 +0100812 if (!ps) {
813 ERRARG;
814 return;
815 }
816
Michal Vaskod09eae62016-02-01 10:32:52 +0100817 for (i = 0; i < ps->session_count; ) {
818 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
819 session = ps->sessions[i];
820 nc_ps_del_session(ps, session);
821 nc_session_free(session);
822 continue;
823 }
824
825 ++i;
826 }
827}
828
Michal Vasko11d142a2016-01-19 15:58:24 +0100829API int
830nc_ctx_lock(int timeout, int *elapsed)
831{
832 return nc_timedlock(&server_opts.ctx_lock, timeout, elapsed);
833}
834
835API int
836nc_ctx_unlock(void)
837{
838 int ret;
839
840 ret = pthread_mutex_unlock(&server_opts.ctx_lock);
841
842 if (ret) {
843 ERR("Mutex unlock failed (%s).", strerror(ret));
844 return -1;
845 }
846
847 return 0;
848}
849
Michal Vasko9e036d52016-01-08 10:49:26 +0100850#if defined(ENABLE_SSH) || defined(ENABLE_TLS)
851
Michal Vasko3031aae2016-01-27 16:07:18 +0100852int
853nc_server_add_endpt_listen(const char *name, const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +0100854{
855 int sock;
Michal Vasko3031aae2016-01-27 16:07:18 +0100856 uint16_t i;
Michal Vasko08a629a2016-02-02 12:20:47 +0100857#ifdef ENABLE_SSH
858 struct nc_server_ssh_opts *ssh_opts;
859#endif
Michal Vasko9e036d52016-01-08 10:49:26 +0100860
Michal Vasko3031aae2016-01-27 16:07:18 +0100861 if (!name || !address || !port) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100862 ERRARG;
863 return -1;
864 }
865
Michal Vasko51e514d2016-02-02 15:51:52 +0100866 /* WRITE LOCK */
867 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100868
869 /* check name uniqueness */
870 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vaskod4c03a82016-02-08 15:27:26 +0100871 if ((server_opts.binds[i].ti == ti) && !strcmp(server_opts.endpts[i].name, name)) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100872 ERR("Endpoint \"%s\" already exists.", name);
Michal Vasko51e514d2016-02-02 15:51:52 +0100873 /* WRITE UNLOCK */
Michal Vasko9faf1c82016-02-01 13:26:19 +0100874 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100875 return -1;
876 }
877 }
878
Michal Vasko9e036d52016-01-08 10:49:26 +0100879 sock = nc_sock_listen(address, port);
880 if (sock == -1) {
Michal Vasko51e514d2016-02-02 15:51:52 +0100881 /* WRITE UNLOCK */
882 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +0100883 return -1;
884 }
885
Michal Vasko3031aae2016-01-27 16:07:18 +0100886 ++server_opts.endpt_count;
887 server_opts.binds = realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
888 server_opts.endpts = realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vasko9e036d52016-01-08 10:49:26 +0100889
Michal Vasko11d142a2016-01-19 15:58:24 +0100890 nc_ctx_lock(-1, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100891 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
892 server_opts.binds[server_opts.endpt_count - 1].address = lydict_insert(server_opts.ctx, address, 0);
Michal Vasko11d142a2016-01-19 15:58:24 +0100893 nc_ctx_unlock();
Michal Vasko3031aae2016-01-27 16:07:18 +0100894 server_opts.binds[server_opts.endpt_count - 1].port = port;
895 server_opts.binds[server_opts.endpt_count - 1].sock = sock;
896 server_opts.binds[server_opts.endpt_count - 1].ti = ti;
897 switch (ti) {
898#ifdef ENABLE_SSH
899 case NC_TI_LIBSSH:
Michal Vasko08a629a2016-02-02 12:20:47 +0100900 ssh_opts = calloc(1, sizeof *ssh_opts);
901 /* set default values */
902 ssh_opts->auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE;
903 ssh_opts->auth_attempts = 3;
904 ssh_opts->auth_timeout = 10;
905
906 server_opts.endpts[server_opts.endpt_count - 1].ti_opts = ssh_opts;
Michal Vasko3031aae2016-01-27 16:07:18 +0100907 break;
908#endif
909#ifdef ENABLE_TLS
910 case NC_TI_OPENSSL:
911 server_opts.endpts[server_opts.endpt_count - 1].ti_opts = calloc(1, sizeof(struct nc_server_tls_opts));
912 break;
913#endif
914 default:
915 ERRINT;
916 server_opts.endpts[server_opts.endpt_count - 1].ti_opts = NULL;
917 break;
918 }
919 pthread_mutex_init(&server_opts.endpts[server_opts.endpt_count - 1].endpt_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +0100920
Michal Vasko3031aae2016-01-27 16:07:18 +0100921 /* WRITE UNLOCK */
922 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +0100923
Michal Vasko9e036d52016-01-08 10:49:26 +0100924 return 0;
925}
926
Michal Vasko3031aae2016-01-27 16:07:18 +0100927int
Michal Vaskoda514772016-02-01 11:32:01 +0100928nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
929{
930 struct nc_endpt *endpt;
931 struct nc_bind *bind = NULL;
932 uint16_t i;
933 int sock;
934
935 if (!endpt_name || (!address && !port) || (address && port) || !ti) {
936 ERRARG;
937 return -1;
938 }
939
Michal Vasko51e514d2016-02-02 15:51:52 +0100940 /* LOCK */
Michal Vaskoda514772016-02-01 11:32:01 +0100941 endpt = nc_server_endpt_lock(endpt_name, ti);
942 if (!endpt) {
943 return -1;
944 }
945
946 /* we need to learn the index, to get the bind :-/ */
947 for (i = 0; i < server_opts.endpt_count; ++i) {
948 if (&server_opts.endpts[i] == endpt) {
949 bind = &server_opts.binds[i];
950 }
951 }
952 if (!bind) {
953 ERRINT;
Michal Vasko51e514d2016-02-02 15:51:52 +0100954 goto fail;
Michal Vaskoda514772016-02-01 11:32:01 +0100955 }
956
957 if (address) {
958 sock = nc_sock_listen(address, bind->port);
959 } else {
960 sock = nc_sock_listen(bind->address, port);
961 }
962 if (sock == -1) {
Michal Vasko51e514d2016-02-02 15:51:52 +0100963 goto fail;
Michal Vaskoda514772016-02-01 11:32:01 +0100964 }
965
966 /* close old socket, update parameters */
967 close(bind->sock);
968 bind->sock = sock;
969 if (address) {
970 lydict_remove(server_opts.ctx, bind->address);
971 bind->address = lydict_insert(server_opts.ctx, address, 0);
972 } else {
973 bind->port = port;
974 }
975
Michal Vasko51e514d2016-02-02 15:51:52 +0100976 /* UNLOCK */
Michal Vasko7a93af72016-02-01 16:00:15 +0100977 nc_server_endpt_unlock(endpt);
Michal Vaskoda514772016-02-01 11:32:01 +0100978 return 0;
Michal Vasko51e514d2016-02-02 15:51:52 +0100979
980fail:
981 /* UNLOCK */
982 nc_server_endpt_unlock(endpt);
983 return -1;
Michal Vaskoda514772016-02-01 11:32:01 +0100984}
985
986int
Michal Vasko3031aae2016-01-27 16:07:18 +0100987nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +0100988{
989 uint32_t i;
990 int ret = -1;
991
Michal Vasko3031aae2016-01-27 16:07:18 +0100992 /* WRITE LOCK */
993 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +0100994
Michal Vasko3031aae2016-01-27 16:07:18 +0100995 if (!name && !ti) {
996 /* remove all */
Michal Vasko3031aae2016-01-27 16:07:18 +0100997 for (i = 0; i < server_opts.endpt_count; ++i) {
Michal Vasko51e514d2016-02-02 15:51:52 +0100998 nc_ctx_lock(-1, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100999 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko11d142a2016-01-19 15:58:24 +01001000 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
Michal Vasko51e514d2016-02-02 15:51:52 +01001001 nc_ctx_unlock();
1002
Michal Vasko3031aae2016-01-27 16:07:18 +01001003 close(server_opts.binds[i].sock);
1004 pthread_mutex_destroy(&server_opts.endpts[i].endpt_lock);
1005 switch (server_opts.binds[i].ti) {
1006#ifdef ENABLE_SSH
1007 case NC_TI_LIBSSH:
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001008 nc_server_ssh_clear_opts(server_opts.endpts[i].ti_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001009 break;
1010#endif
1011#ifdef ENABLE_TLS
1012 case NC_TI_OPENSSL:
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001013 nc_server_tls_clear_opts(server_opts.endpts[i].ti_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001014 break;
1015#endif
1016 default:
1017 ERRINT;
1018 break;
1019 }
1020 free(server_opts.endpts[i].ti_opts);
Michal Vasko9e036d52016-01-08 10:49:26 +01001021
Michal Vasko9e036d52016-01-08 10:49:26 +01001022 ret = 0;
1023 }
Michal Vasko7ddc5702016-02-08 15:29:39 +01001024 free(server_opts.binds);
1025 server_opts.binds = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +01001026 free(server_opts.endpts);
1027 server_opts.endpts = NULL;
1028 server_opts.endpt_count = 0;
1029
Michal Vasko1a38c862016-01-15 15:50:07 +01001030 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +01001031 /* remove one name endpoint or all ti endpoints */
1032 for (i = 0; i < server_opts.endpt_count; ++i) {
1033 if ((server_opts.binds[i].ti == ti) &&
1034 (!name || !strcmp(server_opts.endpts[i].name, name))) {
1035
Michal Vasko11d142a2016-01-19 15:58:24 +01001036 nc_ctx_lock(-1, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +01001037 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko11d142a2016-01-19 15:58:24 +01001038 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
1039 nc_ctx_unlock();
Michal Vasko51e514d2016-02-02 15:51:52 +01001040
Michal Vasko3031aae2016-01-27 16:07:18 +01001041 close(server_opts.binds[i].sock);
1042 pthread_mutex_destroy(&server_opts.endpts[i].endpt_lock);
1043 switch (server_opts.binds[i].ti) {
1044#ifdef ENABLE_SSH
1045 case NC_TI_LIBSSH:
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001046 nc_server_ssh_clear_opts(server_opts.endpts[i].ti_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001047 break;
1048#endif
1049#ifdef ENABLE_TLS
1050 case NC_TI_OPENSSL:
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001051 nc_server_tls_clear_opts(server_opts.endpts[i].ti_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +01001052 break;
1053#endif
1054 default:
1055 ERRINT;
1056 break;
1057 }
1058 free(server_opts.endpts[i].ti_opts);
Michal Vasko1a38c862016-01-15 15:50:07 +01001059
Michal Vasko3031aae2016-01-27 16:07:18 +01001060 --server_opts.endpt_count;
Michal Vaskoc0256492016-02-02 12:19:06 +01001061 if (i < server_opts.endpt_count) {
1062 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
1063 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
1064 } else if (!server_opts.endpt_count) {
1065 free(server_opts.binds);
1066 server_opts.binds = NULL;
1067 free(server_opts.endpts);
1068 server_opts.endpts = NULL;
1069 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001070
1071 ret = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +01001072
1073 if (name) {
1074 /* one name endpoint removed, they are unique, we're done */
1075 break;
1076 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001077 }
1078 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001079 }
1080
Michal Vasko3031aae2016-01-27 16:07:18 +01001081 /* WRITE UNLOCK */
1082 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001083
Michal Vasko9e036d52016-01-08 10:49:26 +01001084 return ret;
1085}
1086
Michal Vasko1a38c862016-01-15 15:50:07 +01001087API int
1088nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001089{
Michal Vasko1a38c862016-01-15 15:50:07 +01001090 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01001091 char *host = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +01001092 uint16_t port, idx;
Michal Vasko9e036d52016-01-08 10:49:26 +01001093
Michal Vasko51e514d2016-02-02 15:51:52 +01001094 if (!server_opts.ctx || !session) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001095 ERRARG;
Michal Vasko1a38c862016-01-15 15:50:07 +01001096 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001097 }
1098
Michal Vasko51e514d2016-02-02 15:51:52 +01001099 /* we have to hold WRITE for the whole time, since there is not
1100 * a way of downgrading the lock to READ */
1101 /* WRITE LOCK */
1102 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
1103
1104 if (!server_opts.endpt_count) {
1105 ERRARG;
1106 /* WRITE UNLOCK */
1107 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1108 return -1;
1109 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001110
Michal Vasko3031aae2016-01-27 16:07:18 +01001111 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &idx);
Michal Vaskob48aa812016-01-18 14:13:09 +01001112
Michal Vasko50456e82016-02-02 12:16:08 +01001113 if (ret < 1) {
Michal Vasko51e514d2016-02-02 15:51:52 +01001114 /* WRITE UNLOCK */
Michal Vasko3031aae2016-01-27 16:07:18 +01001115 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob737d752016-02-09 09:01:27 +01001116 free(host);
Michal Vaskob48aa812016-01-18 14:13:09 +01001117 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001118 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001119 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001120
Michal Vasko1a38c862016-01-15 15:50:07 +01001121 *session = calloc(1, sizeof **session);
Michal Vasko686aa312016-01-21 15:58:18 +01001122 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001123 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001124 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01001125 free(host);
Michal Vasko3031aae2016-01-27 16:07:18 +01001126 ret = -1;
1127 goto fail;
Michal Vasko9e036d52016-01-08 10:49:26 +01001128 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001129 (*session)->status = NC_STATUS_STARTING;
1130 (*session)->side = NC_SERVER;
1131 (*session)->ctx = server_opts.ctx;
1132 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko11d142a2016-01-19 15:58:24 +01001133 nc_ctx_lock(-1, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001134 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
Michal Vasko11d142a2016-01-19 15:58:24 +01001135 nc_ctx_unlock();
Michal Vasko1a38c862016-01-15 15:50:07 +01001136 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01001137
1138 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +01001139 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1140 if (!(*session)->ti_lock) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001141 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001142 close(sock);
Michal Vasko1a38c862016-01-15 15:50:07 +01001143 ret = -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001144 goto fail;
1145 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001146 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001147
Michal Vasko3031aae2016-01-27 16:07:18 +01001148 (*session)->ti_opts = server_opts.endpts[idx].ti_opts;
1149
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001150 /* sock gets assigned to session or closed */
Michal Vasko3d865d22016-01-28 16:00:53 +01001151#ifdef ENABLE_SSH
Michal Vasko3031aae2016-01-27 16:07:18 +01001152 if (server_opts.binds[idx].ti == NC_TI_LIBSSH) {
1153 ret = nc_accept_ssh_session(*session, sock, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001154 if (ret < 1) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001155 goto fail;
1156 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001157 } else
1158#endif
1159#ifdef ENABLE_TLS
1160 if (server_opts.binds[idx].ti == NC_TI_OPENSSL) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001161 ret = nc_accept_tls_session(*session, sock, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001162 if (ret < 1) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001163 goto fail;
1164 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001165 } else
1166#endif
1167 {
Michal Vasko9e036d52016-01-08 10:49:26 +01001168 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001169 close(sock);
Michal Vasko1a38c862016-01-15 15:50:07 +01001170 ret = -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001171 goto fail;
1172 }
1173
Michal Vasko51e514d2016-02-02 15:51:52 +01001174 /* WRITE UNLOCK */
Michal Vasko3031aae2016-01-27 16:07:18 +01001175 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1176
Michal Vaskob48aa812016-01-18 14:13:09 +01001177 /* assign new SID atomically */
1178 /* LOCK */
1179 pthread_spin_lock(&server_opts.sid_lock);
1180 (*session)->id = server_opts.new_session_id++;
1181 /* UNLOCK */
1182 pthread_spin_unlock(&server_opts.sid_lock);
1183
Michal Vasko9e036d52016-01-08 10:49:26 +01001184 /* NETCONF handshake */
Michal Vasko1a38c862016-01-15 15:50:07 +01001185 if (nc_handshake(*session)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001186 nc_session_free(*session);
1187 *session = NULL;
1188 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001189 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001190 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01001191
Michal Vasko1a38c862016-01-15 15:50:07 +01001192 return 1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001193
1194fail:
Michal Vasko3031aae2016-01-27 16:07:18 +01001195 /* WRITE UNLOCK */
1196 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1197
Michal Vasko1a38c862016-01-15 15:50:07 +01001198 nc_session_free(*session);
1199 *session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001200 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001201}
1202
Michal Vasko3031aae2016-01-27 16:07:18 +01001203int
Michal Vaskob05053d2016-01-22 16:12:06 +01001204nc_connect_callhome(const char *host, uint16_t port, NC_TRANSPORT_IMPL ti, int timeout, struct nc_session **session)
1205{
1206 int sock, ret;
1207
Michal Vaskoc61c4492016-01-25 11:13:34 +01001208 if (!host || !port || !ti || !session) {
1209 ERRARG;
1210 return -1;
1211 }
1212
Michal Vaskob05053d2016-01-22 16:12:06 +01001213 sock = nc_sock_connect(host, port);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001214 if (sock < 0) {
1215 return -1;
Michal Vaskob05053d2016-01-22 16:12:06 +01001216 }
1217
1218 *session = calloc(1, sizeof **session);
1219 if (!(*session)) {
1220 ERRMEM;
1221 close(sock);
1222 return -1;
1223 }
1224 (*session)->status = NC_STATUS_STARTING;
1225 (*session)->side = NC_SERVER;
1226 (*session)->ctx = server_opts.ctx;
1227 (*session)->flags = NC_SESSION_SHAREDCTX | NC_SESSION_CALLHOME;
1228 nc_ctx_lock(-1, NULL);
1229 (*session)->host = lydict_insert(server_opts.ctx, host, 0);
1230 nc_ctx_unlock();
1231 (*session)->port = port;
1232
1233 /* transport lock */
1234 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1235 if (!(*session)->ti_lock) {
1236 ERRMEM;
1237 close(sock);
1238 ret = -1;
1239 goto fail;
1240 }
1241 pthread_mutex_init((*session)->ti_lock, NULL);
1242
1243 /* sock gets assigned to session or closed */
Michal Vasko3d865d22016-01-28 16:00:53 +01001244#ifdef ENABLE_SSH
Michal Vaskob05053d2016-01-22 16:12:06 +01001245 if (ti == NC_TI_LIBSSH) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001246 /* OPTS LOCK */
1247 pthread_mutex_lock(&ssh_ch_opts_lock);
1248
Michal Vasko3031aae2016-01-27 16:07:18 +01001249 (*session)->ti_opts = &ssh_ch_opts;
1250 ret = nc_accept_ssh_session(*session, sock, timeout);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001251 (*session)->ti_opts = NULL;
1252
1253 /* OPTS UNLOCK */
1254 pthread_mutex_unlock(&ssh_ch_opts_lock);
1255
Michal Vaskob05053d2016-01-22 16:12:06 +01001256 if (ret < 1) {
1257 goto fail;
1258 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001259 } else
1260#endif
1261#ifdef ENABLE_TLS
1262 if (ti == NC_TI_OPENSSL) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001263 /* OPTS LOCK */
1264 pthread_mutex_lock(&tls_ch_opts_lock);
1265
Michal Vasko3031aae2016-01-27 16:07:18 +01001266 (*session)->ti_opts = &tls_ch_opts;
1267 ret = nc_accept_tls_session(*session, sock, timeout);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001268 (*session)->ti_opts = NULL;
1269
1270 /* OPTS UNLOCK */
1271 pthread_mutex_unlock(&tls_ch_opts_lock);
1272
Michal Vaskob05053d2016-01-22 16:12:06 +01001273 if (ret < 1) {
1274 goto fail;
1275 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001276 } else
1277#endif
1278 {
Michal Vaskob05053d2016-01-22 16:12:06 +01001279 ERRINT;
1280 close(sock);
1281 ret = -1;
1282 goto fail;
1283 }
1284
1285 /* assign new SID atomically */
1286 /* LOCK */
1287 pthread_spin_lock(&server_opts.sid_lock);
1288 (*session)->id = server_opts.new_session_id++;
1289 /* UNLOCK */
1290 pthread_spin_unlock(&server_opts.sid_lock);
1291
1292 /* NETCONF handshake */
1293 if (nc_handshake(*session)) {
1294 ret = -1;
1295 goto fail;
1296 }
1297 (*session)->status = NC_STATUS_RUNNING;
1298
1299 return 1;
1300
1301fail:
1302 nc_session_free(*session);
1303 *session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001304 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +01001305}
1306
Michal Vasko9e036d52016-01-08 10:49:26 +01001307#endif /* ENABLE_SSH || ENABLE_TLS */