blob: 94c52f18517d34d92220d15cfbcff766b9f68a63 [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 */
85 pthread_rwlock_rdlock(&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 Vasko0473c4c2016-01-19 10:40:06 +0100307 module = ly_ctx_get_module(server_opts.ctx, "ietf-netconf-monitoring", NULL);
308 if (module) {
309 sdata = lys_get_node(module, "/get-schema/output/data");
310 }
311 if (model_data && sdata) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100312 nc_ctx_lock(-1, NULL);
Michal Vasko05ba9df2016-01-13 14:40:27 +0100313 data = lyd_output_new_anyxml(sdata, model_data);
Michal Vasko11d142a2016-01-19 15:58:24 +0100314 nc_ctx_unlock();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100315 }
316 free(model_data);
317 if (!data) {
318 ERRINT;
319 return NULL;
320 }
321
322 return nc_server_reply_data(data, NC_PARAMTYPE_FREE);
323}
324
325static struct nc_server_reply *
Michal Vasko428087d2016-01-14 16:04:28 +0100326nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *session)
Michal Vasko05ba9df2016-01-13 14:40:27 +0100327{
Michal Vasko428087d2016-01-14 16:04:28 +0100328 session->term_reason = NC_SESSION_TERM_CLOSED;
329 return nc_server_reply_ok();
Michal Vasko05ba9df2016-01-13 14:40:27 +0100330}
331
Michal Vasko086311b2016-01-08 09:53:11 +0100332API int
333nc_server_init(struct ly_ctx *ctx)
334{
Michal Vasko05ba9df2016-01-13 14:40:27 +0100335 const struct lys_node *rpc;
Michal Vasko0473c4c2016-01-19 10:40:06 +0100336 const struct lys_module *mod;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100337
Michal Vasko086311b2016-01-08 09:53:11 +0100338 if (!ctx) {
339 ERRARG;
340 return -1;
341 }
342
Michal Vasko05ba9df2016-01-13 14:40:27 +0100343 /* set default <get-schema> callback if not specified */
Michal Vasko0473c4c2016-01-19 10:40:06 +0100344 rpc = NULL;
345 mod = ly_ctx_get_module(ctx, "ietf-netconf-monitoring", NULL);
346 if (mod) {
347 rpc = lys_get_node(mod, "/get-schema");
348 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100349 if (rpc && !rpc->private) {
350 lys_set_private(rpc, nc_clb_default_get_schema);
351 }
352
353 /* set default <close-session> callback if not specififed */
Michal Vasko0473c4c2016-01-19 10:40:06 +0100354 rpc = NULL;
355 mod = ly_ctx_get_module(ctx, "ietf-netconf", NULL);
356 if (mod) {
357 rpc = lys_get_node(mod, "/close-session");
358 }
Michal Vasko05ba9df2016-01-13 14:40:27 +0100359 if (rpc && !rpc->private) {
360 lys_set_private(rpc, nc_clb_default_close_session);
361 }
362
Michal Vasko086311b2016-01-08 09:53:11 +0100363 server_opts.ctx = ctx;
Michal Vaskob48aa812016-01-18 14:13:09 +0100364
365 server_opts.new_session_id = 1;
366 pthread_spin_init(&server_opts.sid_lock, PTHREAD_PROCESS_PRIVATE);
367
Michal Vasko086311b2016-01-08 09:53:11 +0100368 return 0;
369}
370
Michal Vaskob48aa812016-01-18 14:13:09 +0100371API void
372nc_server_destroy(void)
373{
374 pthread_spin_destroy(&server_opts.sid_lock);
375
376#if defined(ENABLE_SSH) || defined(ENABLE_TLS)
Michal Vasko3031aae2016-01-27 16:07:18 +0100377 nc_server_del_endpt(NULL, 0);
Michal Vaskob48aa812016-01-18 14:13:09 +0100378#endif
379}
380
Michal Vasko086311b2016-01-08 09:53:11 +0100381API int
382nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
383{
384 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)
385 || (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT)))) {
386 ERRARG;
387 return -1;
388 }
389
390 server_opts.wd_basic_mode = basic_mode;
391 server_opts.wd_also_supported = also_supported;
392 return 0;
393}
394
Michal Vasko1a38c862016-01-15 15:50:07 +0100395API void
Michal Vasko086311b2016-01-08 09:53:11 +0100396nc_server_set_capab_interleave(int interleave_support)
397{
398 if (interleave_support) {
399 server_opts.interleave_capab = 1;
400 } else {
401 server_opts.interleave_capab = 0;
402 }
Michal Vasko086311b2016-01-08 09:53:11 +0100403}
404
Michal Vasko1a38c862016-01-15 15:50:07 +0100405API void
Michal Vasko086311b2016-01-08 09:53:11 +0100406nc_server_set_hello_timeout(uint16_t hello_timeout)
407{
Michal Vasko086311b2016-01-08 09:53:11 +0100408 server_opts.hello_timeout = hello_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100409}
410
Michal Vasko1a38c862016-01-15 15:50:07 +0100411API void
Michal Vasko086311b2016-01-08 09:53:11 +0100412nc_server_set_idle_timeout(uint16_t idle_timeout)
413{
Michal Vasko086311b2016-01-08 09:53:11 +0100414 server_opts.idle_timeout = idle_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100415}
416
417API int
Michal Vasko1a38c862016-01-15 15:50:07 +0100418nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +0100419{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100420 if (!server_opts.ctx || (fdin < 0) || (fdout < 0) || !username || !session) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100421 ERRARG;
422 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100423 }
424
425 /* prepare session structure */
Michal Vasko1a38c862016-01-15 15:50:07 +0100426 *session = calloc(1, sizeof **session);
427 if (!(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100428 ERRMEM;
Michal Vasko1a38c862016-01-15 15:50:07 +0100429 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100430 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100431 (*session)->status = NC_STATUS_STARTING;
432 (*session)->side = NC_SERVER;
Michal Vasko086311b2016-01-08 09:53:11 +0100433
434 /* transport specific data */
Michal Vasko1a38c862016-01-15 15:50:07 +0100435 (*session)->ti_type = NC_TI_FD;
436 (*session)->ti.fd.in = fdin;
437 (*session)->ti.fd.out = fdout;
Michal Vasko086311b2016-01-08 09:53:11 +0100438
439 /* assign context (dicionary needed for handshake) */
Michal Vasko1a38c862016-01-15 15:50:07 +0100440 (*session)->flags = NC_SESSION_SHAREDCTX;
441 (*session)->ctx = server_opts.ctx;
Michal Vasko086311b2016-01-08 09:53:11 +0100442
Michal Vaskob48aa812016-01-18 14:13:09 +0100443 /* assign new SID atomically */
444 pthread_spin_lock(&server_opts.sid_lock);
445 (*session)->id = server_opts.new_session_id++;
446 pthread_spin_unlock(&server_opts.sid_lock);
447
Michal Vasko086311b2016-01-08 09:53:11 +0100448 /* NETCONF handshake */
Michal Vasko1a38c862016-01-15 15:50:07 +0100449 if (nc_handshake(*session)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100450 goto fail;
451 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100452 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100453 (*session)->last_rpc = time(NULL);
Michal Vasko086311b2016-01-08 09:53:11 +0100454
Michal Vasko1a38c862016-01-15 15:50:07 +0100455 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100456
457fail:
Michal Vasko1a38c862016-01-15 15:50:07 +0100458 nc_session_free(*session);
459 *session = NULL;
460 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100461}
Michal Vasko9e036d52016-01-08 10:49:26 +0100462
Michal Vasko428087d2016-01-14 16:04:28 +0100463API struct nc_pollsession *
464nc_ps_new(void)
465{
466 return calloc(1, sizeof(struct nc_pollsession));
467}
468
469API void
470nc_ps_free(struct nc_pollsession *ps)
471{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100472 if (!ps) {
473 return;
474 }
475
Michal Vasko3a715132016-01-21 15:40:31 +0100476 free(ps->pfds);
Michal Vasko428087d2016-01-14 16:04:28 +0100477 free(ps->sessions);
478 free(ps);
479}
480
481API int
482nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session)
483{
484 if (!ps || !session) {
485 ERRARG;
486 return -1;
487 }
488
489 ++ps->session_count;
Michal Vasko3a715132016-01-21 15:40:31 +0100490 ps->pfds = realloc(ps->pfds, ps->session_count * sizeof *ps->pfds);
Michal Vasko428087d2016-01-14 16:04:28 +0100491 ps->sessions = realloc(ps->sessions, ps->session_count * sizeof *ps->sessions);
492
493 switch (session->ti_type) {
494 case NC_TI_FD:
Michal Vasko3a715132016-01-21 15:40:31 +0100495 ps->pfds[ps->session_count - 1].fd = session->ti.fd.in;
Michal Vasko428087d2016-01-14 16:04:28 +0100496 break;
497
498#ifdef ENABLE_SSH
499 case NC_TI_LIBSSH:
Michal Vasko3a715132016-01-21 15:40:31 +0100500 ps->pfds[ps->session_count - 1].fd = ssh_get_fd(session->ti.libssh.session);
Michal Vasko428087d2016-01-14 16:04:28 +0100501 break;
502#endif
503
504#ifdef ENABLE_TLS
505 case NC_TI_OPENSSL:
Michal Vasko3a715132016-01-21 15:40:31 +0100506 ps->pfds[ps->session_count - 1].fd = SSL_get_rfd(session->ti.tls);
Michal Vasko428087d2016-01-14 16:04:28 +0100507 break;
508#endif
509
510 default:
511 ERRINT;
512 return -1;
513 }
Michal Vasko3a715132016-01-21 15:40:31 +0100514 ps->pfds[ps->session_count - 1].events = POLLIN;
515 ps->pfds[ps->session_count - 1].revents = 0;
516 ps->sessions[ps->session_count - 1] = session;
Michal Vasko428087d2016-01-14 16:04:28 +0100517
518 return 0;
519}
520
521API int
522nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session)
523{
524 uint16_t i;
525
526 if (!ps || !session) {
527 ERRARG;
528 return -1;
529 }
530
531 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100532 if (ps->sessions[i] == session) {
Michal Vasko428087d2016-01-14 16:04:28 +0100533 --ps->session_count;
Michal Vasko3a715132016-01-21 15:40:31 +0100534 ps->sessions[i] = ps->sessions[ps->session_count];
535 memcpy(&ps->pfds[i], &ps->pfds[ps->session_count], sizeof *ps->pfds);
Michal Vasko428087d2016-01-14 16:04:28 +0100536 return 0;
537 }
538 }
539
Michal Vaskof0537d82016-01-29 14:42:38 +0100540 return -1;
Michal Vasko428087d2016-01-14 16:04:28 +0100541}
542
543/* must be called holding the session lock! */
544static NC_MSG_TYPE
545nc_recv_rpc(struct nc_session *session, struct nc_server_rpc **rpc)
546{
547 struct lyxml_elem *xml = NULL;
548 NC_MSG_TYPE msgtype;
549
550 if (!session || !rpc) {
551 ERRARG;
552 return NC_MSG_ERROR;
553 } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100554 ERR("Session %u: invalid session to receive RPCs.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100555 return NC_MSG_ERROR;
556 }
557
558 msgtype = nc_read_msg(session, &xml);
559
560 switch (msgtype) {
561 case NC_MSG_RPC:
562 *rpc = malloc(sizeof **rpc);
Michal Vasko11d142a2016-01-19 15:58:24 +0100563 nc_ctx_lock(-1, NULL);
Michal Vasko428087d2016-01-14 16:04:28 +0100564 (*rpc)->tree = lyd_parse_xml(server_opts.ctx, &xml->child, LYD_OPT_DESTRUCT | LYD_OPT_RPC);
Michal Vasko11d142a2016-01-19 15:58:24 +0100565 nc_ctx_unlock();
Michal Vasko428087d2016-01-14 16:04:28 +0100566 (*rpc)->root = xml;
567 break;
568 case NC_MSG_HELLO:
Michal Vaskod083db62016-01-19 10:31:29 +0100569 ERR("Session %u: received another <hello> message.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100570 goto error;
571 case NC_MSG_REPLY:
Michal Vaskod083db62016-01-19 10:31:29 +0100572 ERR("Session %u: received <rpc-reply> from NETCONF client.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100573 goto error;
574 case NC_MSG_NOTIF:
Michal Vaskod083db62016-01-19 10:31:29 +0100575 ERR("Session %u: received <notification> from NETCONF client.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100576 goto error;
577 default:
578 /* NC_MSG_ERROR - pass it out;
579 * NC_MSG_WOULDBLOCK and NC_MSG_NONE is not returned by nc_read_msg()
580 */
581 break;
582 }
583
584 return msgtype;
585
586error:
587 /* cleanup */
588 lyxml_free(server_opts.ctx, xml);
589
590 return NC_MSG_ERROR;
591}
592
593/* must be called holding the session lock! */
594static NC_MSG_TYPE
595nc_send_reply(struct nc_session *session, struct nc_server_rpc *rpc)
596{
597 nc_rpc_clb clb;
598 struct nc_server_reply *reply;
599 int ret;
600
601 /* no callback, reply with a not-implemented error */
602 if (!rpc->tree->schema->private) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100603 reply = nc_server_reply_err(nc_err(NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_PROT));
Michal Vasko428087d2016-01-14 16:04:28 +0100604 } else {
605 clb = (nc_rpc_clb)rpc->tree->schema->private;
606 reply = clb(rpc->tree, session);
607 }
608
609 if (!reply) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100610 reply = nc_server_reply_err(nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP));
Michal Vasko428087d2016-01-14 16:04:28 +0100611 }
612
613 ret = nc_write_msg(session, NC_MSG_REPLY, rpc->root, reply);
614
615 /* special case if term_reason was set in callback, last reply was sent (needed for <close-session> if nothing else) */
616 if ((session->status == NC_STATUS_RUNNING) && (session->term_reason != NC_SESSION_TERM_NONE)) {
617 session->status = NC_STATUS_INVALID;
618 }
619
620 if (ret == -1) {
Michal Vaskod083db62016-01-19 10:31:29 +0100621 ERR("Session %u: failed to write reply.", session->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100622 nc_server_reply_free(reply);
623 return NC_MSG_ERROR;
624 }
625 nc_server_reply_free(reply);
626
627 return NC_MSG_REPLY;
628}
629
630API int
631nc_ps_poll(struct nc_pollsession *ps, int timeout)
632{
633 int ret;
Michal Vasko3512e402016-01-28 16:22:34 +0100634 uint16_t i;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100635 time_t cur_time;
Michal Vasko428087d2016-01-14 16:04:28 +0100636 NC_MSG_TYPE msgtype;
637 struct nc_session *session;
638 struct nc_server_rpc *rpc;
Michal Vasko96164bf2016-01-21 15:41:58 +0100639 struct timespec old_ts;
Michal Vasko428087d2016-01-14 16:04:28 +0100640
641 if (!ps || !ps->session_count) {
642 ERRARG;
643 return -1;
644 }
645
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100646 cur_time = time(NULL);
647
Michal Vasko428087d2016-01-14 16:04:28 +0100648 for (i = 0; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100649 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
650 ERR("Session %u: session not running.", ps->sessions[i]->id);
Michal Vasko428087d2016-01-14 16:04:28 +0100651 return -1;
652 }
Michal Vaskobd8ef262016-01-20 11:09:27 +0100653
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100654 /* TODO invalidate only sessions without subscription */
Michal Vasko3a715132016-01-21 15:40:31 +0100655 if (server_opts.idle_timeout && (ps->sessions[i]->last_rpc + server_opts.idle_timeout >= cur_time)) {
656 ERR("Session %u: session idle timeout elapsed.", ps->sessions[i]->id);
657 ps->sessions[i]->status = NC_STATUS_INVALID;
658 ps->sessions[i]->term_reason = NC_SESSION_TERM_TIMEOUT;
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100659 return 3;
660 }
661
Michal Vasko3a715132016-01-21 15:40:31 +0100662 if (ps->pfds[i].revents) {
Michal Vaskobd8ef262016-01-20 11:09:27 +0100663 break;
664 }
Michal Vasko428087d2016-01-14 16:04:28 +0100665 }
666
667 if (timeout > 0) {
668 clock_gettime(CLOCK_MONOTONIC_RAW, &old_ts);
669 }
670
Michal Vaskobd8ef262016-01-20 11:09:27 +0100671 if (i == ps->session_count) {
Michal Vasko3512e402016-01-28 16:22:34 +0100672#ifdef ENABLE_SSH
Michal Vasko3a715132016-01-21 15:40:31 +0100673retry_poll:
Michal Vasko3512e402016-01-28 16:22:34 +0100674#endif
Michal Vaskobd8ef262016-01-20 11:09:27 +0100675 /* no leftover event */
676 i = 0;
Michal Vasko3a715132016-01-21 15:40:31 +0100677 ret = poll(ps->pfds, ps->session_count, timeout);
Michal Vaskobd8ef262016-01-20 11:09:27 +0100678 if (ret < 1) {
679 return ret;
680 }
Michal Vasko428087d2016-01-14 16:04:28 +0100681 }
682
Michal Vaskobd8ef262016-01-20 11:09:27 +0100683 /* find the first fd with POLLIN, we don't care if there are more now */
684 for (; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100685 if (ps->pfds[i].revents & POLLHUP) {
686 ERR("Session %u: communication socket unexpectedly closed.", ps->sessions[i]->id);
687 ps->sessions[i]->status = NC_STATUS_INVALID;
688 ps->sessions[i]->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vaskobd8ef262016-01-20 11:09:27 +0100689 return 3;
Michal Vasko3a715132016-01-21 15:40:31 +0100690 } else if (ps->pfds[i].revents & POLLERR) {
691 ERR("Session %u: communication socket error.", ps->sessions[i]->id);
692 ps->sessions[i]->status = NC_STATUS_INVALID;
693 ps->sessions[i]->term_reason = NC_SESSION_TERM_OTHER;
Michal Vaskobd8ef262016-01-20 11:09:27 +0100694 return 3;
Michal Vasko3a715132016-01-21 15:40:31 +0100695 } else if (ps->pfds[i].revents & POLLIN) {
Michal Vasko428087d2016-01-14 16:04:28 +0100696#ifdef ENABLE_SSH
Michal Vasko96164bf2016-01-21 15:41:58 +0100697 if (ps->sessions[i]->ti_type == NC_TI_LIBSSH) {
Michal Vasko3512e402016-01-28 16:22:34 +0100698 uint16_t j;
699
Michal Vasko96164bf2016-01-21 15:41:58 +0100700 /* things are not that simple with SSH... */
701 ret = nc_ssh_pollin(ps->sessions[i], &timeout);
702
703 /* clear POLLIN on sessions sharing this session's SSH session */
704 if ((ret == 1) || (ret >= 4)) {
705 for (j = i + 1; j < ps->session_count; ++j) {
706 if (ps->pfds[j].fd == ps->pfds[i].fd) {
707 ps->pfds[j].revents = 0;
708 }
709 }
710 }
711
712 /* actual event happened */
713 if ((ret <= 0) || (ret >= 3)) {
714 ps->pfds[i].revents = 0;
715 return ret;
716
717 /* event occurred on some other channel */
718 } else if (ret == 2) {
719 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100720 if (i == ps->session_count - 1) {
721 /* last session and it is not the right channel, ... */
722 if (timeout > 0) {
723 /* ... decrease timeout, wait it all out and try again, last time */
Michal Vasko96164bf2016-01-21 15:41:58 +0100724 nc_subtract_elapsed(&timeout, &old_ts);
725 usleep(timeout * 1000);
726 timeout = 0;
727 goto retry_poll;
Michal Vasko428087d2016-01-14 16:04:28 +0100728 } else if (!timeout) {
729 /* ... timeout is 0, so that is it */
730 return 0;
731 } else {
732 /* ... retry polling reasonable time apart */
733 usleep(NC_TIMEOUT_STEP);
734 goto retry_poll;
735 }
736 }
737 /* check other sessions */
738 continue;
Michal Vasko428087d2016-01-14 16:04:28 +0100739 }
740 }
741#endif /* ENABLE_SSH */
742
Michal Vaskobd8ef262016-01-20 11:09:27 +0100743 /* we are going to process it now */
Michal Vasko3a715132016-01-21 15:40:31 +0100744 ps->pfds[i].revents = 0;
Michal Vasko428087d2016-01-14 16:04:28 +0100745 break;
746 }
747 }
748
749 if (i == ps->session_count) {
750 ERRINT;
751 return -1;
752 }
753
754 /* this is the session with some data available for reading */
Michal Vasko3a715132016-01-21 15:40:31 +0100755 session = ps->sessions[i];
Michal Vasko428087d2016-01-14 16:04:28 +0100756
757 if (timeout > 0) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100758 nc_subtract_elapsed(&timeout, &old_ts);
Michal Vasko428087d2016-01-14 16:04:28 +0100759 }
760
Michal Vaskobd8ef262016-01-20 11:09:27 +0100761 /* reading an RPC and sending a reply must be atomic (no other RPC should be read) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100762 ret = nc_timedlock(session->ti_lock, timeout, NULL);
763 if (ret != 1) {
764 /* error or timeout */
765 return ret;
Michal Vasko428087d2016-01-14 16:04:28 +0100766 }
767
768 msgtype = nc_recv_rpc(session, &rpc);
769 if (msgtype == NC_MSG_ERROR) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100770 pthread_mutex_unlock(session->ti_lock);
Michal Vasko428087d2016-01-14 16:04:28 +0100771 if (session->status != NC_STATUS_RUNNING) {
Michal Vaskobd8ef262016-01-20 11:09:27 +0100772 return 3;
Michal Vasko428087d2016-01-14 16:04:28 +0100773 }
774 return -1;
775 }
776
777 /* process RPC */
Michal Vasko5e6f4cc2016-01-20 13:27:44 +0100778 session->last_rpc = time(NULL);
Michal Vasko428087d2016-01-14 16:04:28 +0100779 msgtype = nc_send_reply(session, rpc);
780
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100781 pthread_mutex_unlock(session->ti_lock);
Michal Vasko428087d2016-01-14 16:04:28 +0100782
783 if (msgtype == NC_MSG_ERROR) {
784 nc_server_rpc_free(rpc);
Michal Vasko428087d2016-01-14 16:04:28 +0100785 return -1;
786 }
Michal Vasko428087d2016-01-14 16:04:28 +0100787 nc_server_rpc_free(rpc);
Michal Vaskobd8ef262016-01-20 11:09:27 +0100788
Michal Vaskobd8b4e12016-01-22 16:11:20 +0100789 /* status change takes precedence over leftover events (return 2) */
790 if (session->status != NC_STATUS_RUNNING) {
791 return 3;
792 }
793
Michal Vaskobd8ef262016-01-20 11:09:27 +0100794 /* is there some other socket waiting? */
795 for (++i; i < ps->session_count; ++i) {
Michal Vasko3a715132016-01-21 15:40:31 +0100796 if (ps->pfds[i].revents) {
Michal Vaskobd8ef262016-01-20 11:09:27 +0100797 return 2;
798 }
799 }
800
Michal Vasko428087d2016-01-14 16:04:28 +0100801 return 1;
802}
803
Michal Vaskod09eae62016-02-01 10:32:52 +0100804API void
805nc_ps_clear(struct nc_pollsession *ps)
806{
807 uint16_t i;
808 struct nc_session *session;
809
Michal Vasko9a25e932016-02-01 10:36:42 +0100810 if (!ps) {
811 ERRARG;
812 return;
813 }
814
Michal Vaskod09eae62016-02-01 10:32:52 +0100815 for (i = 0; i < ps->session_count; ) {
816 if (ps->sessions[i]->status != NC_STATUS_RUNNING) {
817 session = ps->sessions[i];
818 nc_ps_del_session(ps, session);
819 nc_session_free(session);
820 continue;
821 }
822
823 ++i;
824 }
825}
826
Michal Vasko11d142a2016-01-19 15:58:24 +0100827API int
828nc_ctx_lock(int timeout, int *elapsed)
829{
830 return nc_timedlock(&server_opts.ctx_lock, timeout, elapsed);
831}
832
833API int
834nc_ctx_unlock(void)
835{
836 int ret;
837
838 ret = pthread_mutex_unlock(&server_opts.ctx_lock);
839
840 if (ret) {
841 ERR("Mutex unlock failed (%s).", strerror(ret));
842 return -1;
843 }
844
845 return 0;
846}
847
Michal Vasko9e036d52016-01-08 10:49:26 +0100848#if defined(ENABLE_SSH) || defined(ENABLE_TLS)
849
Michal Vasko3031aae2016-01-27 16:07:18 +0100850int
851nc_server_add_endpt_listen(const char *name, const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +0100852{
853 int sock;
Michal Vasko3031aae2016-01-27 16:07:18 +0100854 uint16_t i;
Michal Vasko9e036d52016-01-08 10:49:26 +0100855
Michal Vasko3031aae2016-01-27 16:07:18 +0100856 if (!name || !address || !port) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100857 ERRARG;
858 return -1;
859 }
860
Michal Vasko3031aae2016-01-27 16:07:18 +0100861 /* READ LOCK */
862 pthread_rwlock_rdlock(&server_opts.endpt_array_lock);
863
864 /* check name uniqueness */
865 for (i = 0; i < server_opts.endpt_count; ++i) {
866 if (!strcmp(server_opts.endpts[i].name, name)) {
867 ERR("Endpoint \"%s\" already exists.", name);
868 return -1;
869 }
870 }
871
872 /* READ UNLOCK */
873 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
874
Michal Vasko9e036d52016-01-08 10:49:26 +0100875 sock = nc_sock_listen(address, port);
876 if (sock == -1) {
877 return -1;
878 }
879
Michal Vasko3031aae2016-01-27 16:07:18 +0100880 /* WRITE LOCK */
881 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +0100882
Michal Vasko3031aae2016-01-27 16:07:18 +0100883 ++server_opts.endpt_count;
884 server_opts.binds = realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds);
885 server_opts.endpts = realloc(server_opts.endpts, server_opts.endpt_count * sizeof *server_opts.endpts);
Michal Vasko9e036d52016-01-08 10:49:26 +0100886
Michal Vasko11d142a2016-01-19 15:58:24 +0100887 nc_ctx_lock(-1, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100888 server_opts.endpts[server_opts.endpt_count - 1].name = lydict_insert(server_opts.ctx, name, 0);
889 server_opts.binds[server_opts.endpt_count - 1].address = lydict_insert(server_opts.ctx, address, 0);
Michal Vasko11d142a2016-01-19 15:58:24 +0100890 nc_ctx_unlock();
Michal Vasko3031aae2016-01-27 16:07:18 +0100891 server_opts.binds[server_opts.endpt_count - 1].port = port;
892 server_opts.binds[server_opts.endpt_count - 1].sock = sock;
893 server_opts.binds[server_opts.endpt_count - 1].ti = ti;
894 switch (ti) {
895#ifdef ENABLE_SSH
896 case NC_TI_LIBSSH:
897 server_opts.endpts[server_opts.endpt_count - 1].ti_opts = calloc(1, sizeof(struct nc_server_ssh_opts));
898 break;
899#endif
900#ifdef ENABLE_TLS
901 case NC_TI_OPENSSL:
902 server_opts.endpts[server_opts.endpt_count - 1].ti_opts = calloc(1, sizeof(struct nc_server_tls_opts));
903 break;
904#endif
905 default:
906 ERRINT;
907 server_opts.endpts[server_opts.endpt_count - 1].ti_opts = NULL;
908 break;
909 }
910 pthread_mutex_init(&server_opts.endpts[server_opts.endpt_count - 1].endpt_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +0100911
Michal Vasko3031aae2016-01-27 16:07:18 +0100912 /* WRITE UNLOCK */
913 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +0100914
Michal Vasko9e036d52016-01-08 10:49:26 +0100915 return 0;
916}
917
Michal Vasko3031aae2016-01-27 16:07:18 +0100918int
919nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti)
Michal Vasko9e036d52016-01-08 10:49:26 +0100920{
921 uint32_t i;
922 int ret = -1;
923
Michal Vasko3031aae2016-01-27 16:07:18 +0100924 /* WRITE LOCK */
925 pthread_rwlock_wrlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +0100926
Michal Vasko3031aae2016-01-27 16:07:18 +0100927 if (!name && !ti) {
928 /* remove all */
Michal Vasko11d142a2016-01-19 15:58:24 +0100929 nc_ctx_lock(-1, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100930 for (i = 0; i < server_opts.endpt_count; ++i) {
931 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko11d142a2016-01-19 15:58:24 +0100932 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
Michal Vasko3031aae2016-01-27 16:07:18 +0100933 close(server_opts.binds[i].sock);
934 pthread_mutex_destroy(&server_opts.endpts[i].endpt_lock);
935 switch (server_opts.binds[i].ti) {
936#ifdef ENABLE_SSH
937 case NC_TI_LIBSSH:
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100938 nc_server_ssh_clear_opts(server_opts.endpts[i].ti_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100939 break;
940#endif
941#ifdef ENABLE_TLS
942 case NC_TI_OPENSSL:
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100943 nc_server_tls_clear_opts(server_opts.endpts[i].ti_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100944 break;
945#endif
946 default:
947 ERRINT;
948 break;
949 }
950 free(server_opts.endpts[i].ti_opts);
Michal Vasko9e036d52016-01-08 10:49:26 +0100951
Michal Vasko9e036d52016-01-08 10:49:26 +0100952 ret = 0;
953 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100954 nc_ctx_unlock();
Michal Vasko3031aae2016-01-27 16:07:18 +0100955 free(server_opts.endpts);
956 server_opts.endpts = NULL;
957 server_opts.endpt_count = 0;
958
Michal Vasko1a38c862016-01-15 15:50:07 +0100959 } else {
Michal Vasko3031aae2016-01-27 16:07:18 +0100960 /* remove one name endpoint or all ti endpoints */
961 for (i = 0; i < server_opts.endpt_count; ++i) {
962 if ((server_opts.binds[i].ti == ti) &&
963 (!name || !strcmp(server_opts.endpts[i].name, name))) {
964
Michal Vasko11d142a2016-01-19 15:58:24 +0100965 nc_ctx_lock(-1, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100966 lydict_remove(server_opts.ctx, server_opts.endpts[i].name);
Michal Vasko11d142a2016-01-19 15:58:24 +0100967 lydict_remove(server_opts.ctx, server_opts.binds[i].address);
968 nc_ctx_unlock();
Michal Vasko3031aae2016-01-27 16:07:18 +0100969 close(server_opts.binds[i].sock);
970 pthread_mutex_destroy(&server_opts.endpts[i].endpt_lock);
971 switch (server_opts.binds[i].ti) {
972#ifdef ENABLE_SSH
973 case NC_TI_LIBSSH:
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100974 nc_server_ssh_clear_opts(server_opts.endpts[i].ti_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100975 break;
976#endif
977#ifdef ENABLE_TLS
978 case NC_TI_OPENSSL:
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100979 nc_server_tls_clear_opts(server_opts.endpts[i].ti_opts);
Michal Vasko3031aae2016-01-27 16:07:18 +0100980 break;
981#endif
982 default:
983 ERRINT;
984 break;
985 }
986 free(server_opts.endpts[i].ti_opts);
Michal Vasko1a38c862016-01-15 15:50:07 +0100987
Michal Vasko3031aae2016-01-27 16:07:18 +0100988 --server_opts.endpt_count;
989 memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds);
990 memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts);
Michal Vasko1a38c862016-01-15 15:50:07 +0100991
992 ret = 0;
Michal Vasko3031aae2016-01-27 16:07:18 +0100993
994 if (name) {
995 /* one name endpoint removed, they are unique, we're done */
996 break;
997 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100998 }
999 }
Michal Vasko9e036d52016-01-08 10:49:26 +01001000 }
1001
Michal Vasko3031aae2016-01-27 16:07:18 +01001002 /* WRITE UNLOCK */
1003 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001004
Michal Vasko9e036d52016-01-08 10:49:26 +01001005 return ret;
1006}
1007
Michal Vasko1a38c862016-01-15 15:50:07 +01001008API int
1009nc_accept(int timeout, struct nc_session **session)
Michal Vasko9e036d52016-01-08 10:49:26 +01001010{
Michal Vasko1a38c862016-01-15 15:50:07 +01001011 int sock, ret;
Michal Vasko5c2f7952016-01-22 13:16:31 +01001012 char *host = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +01001013 uint16_t port, idx;
Michal Vasko9e036d52016-01-08 10:49:26 +01001014
Michal Vasko3031aae2016-01-27 16:07:18 +01001015 if (!server_opts.ctx || !server_opts.endpt_count || !session) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001016 ERRARG;
Michal Vasko1a38c862016-01-15 15:50:07 +01001017 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001018 }
1019
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001020 /* READ LOCK */
1021 pthread_rwlock_rdlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001022
Michal Vasko3031aae2016-01-27 16:07:18 +01001023 ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &idx);
Michal Vaskob48aa812016-01-18 14:13:09 +01001024
Michal Vasko11d142a2016-01-19 15:58:24 +01001025 if (ret < 0) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001026 /* READ UNLOCK */
Michal Vasko3031aae2016-01-27 16:07:18 +01001027 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001028 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001029 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001030 sock = ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001031
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001032 /* ENDPT LOCK */
1033 pthread_mutex_lock(&server_opts.endpts[idx].endpt_lock);
1034
Michal Vasko1a38c862016-01-15 15:50:07 +01001035 *session = calloc(1, sizeof **session);
Michal Vasko686aa312016-01-21 15:58:18 +01001036 if (!(*session)) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001037 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001038 close(sock);
Michal Vasko5c2f7952016-01-22 13:16:31 +01001039 free(host);
Michal Vasko3031aae2016-01-27 16:07:18 +01001040 ret = -1;
1041 goto fail;
Michal Vasko9e036d52016-01-08 10:49:26 +01001042 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001043 (*session)->status = NC_STATUS_STARTING;
1044 (*session)->side = NC_SERVER;
1045 (*session)->ctx = server_opts.ctx;
1046 (*session)->flags = NC_SESSION_SHAREDCTX;
Michal Vasko11d142a2016-01-19 15:58:24 +01001047 nc_ctx_lock(-1, NULL);
Michal Vasko1a38c862016-01-15 15:50:07 +01001048 (*session)->host = lydict_insert_zc(server_opts.ctx, host);
Michal Vasko11d142a2016-01-19 15:58:24 +01001049 nc_ctx_unlock();
Michal Vasko1a38c862016-01-15 15:50:07 +01001050 (*session)->port = port;
Michal Vasko9e036d52016-01-08 10:49:26 +01001051
1052 /* transport lock */
Michal Vasko1a38c862016-01-15 15:50:07 +01001053 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1054 if (!(*session)->ti_lock) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001055 ERRMEM;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001056 close(sock);
Michal Vasko1a38c862016-01-15 15:50:07 +01001057 ret = -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001058 goto fail;
1059 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001060 pthread_mutex_init((*session)->ti_lock, NULL);
Michal Vasko9e036d52016-01-08 10:49:26 +01001061
Michal Vasko3031aae2016-01-27 16:07:18 +01001062 (*session)->ti_opts = server_opts.endpts[idx].ti_opts;
1063
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001064 /* sock gets assigned to session or closed */
Michal Vasko3d865d22016-01-28 16:00:53 +01001065#ifdef ENABLE_SSH
Michal Vasko3031aae2016-01-27 16:07:18 +01001066 if (server_opts.binds[idx].ti == NC_TI_LIBSSH) {
1067 ret = nc_accept_ssh_session(*session, sock, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001068 if (ret < 1) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001069 goto fail;
1070 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001071 } else
1072#endif
1073#ifdef ENABLE_TLS
1074 if (server_opts.binds[idx].ti == NC_TI_OPENSSL) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001075 ret = nc_accept_tls_session(*session, sock, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001076 if (ret < 1) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001077 goto fail;
1078 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001079 } else
1080#endif
1081 {
Michal Vasko9e036d52016-01-08 10:49:26 +01001082 ERRINT;
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001083 close(sock);
Michal Vasko1a38c862016-01-15 15:50:07 +01001084 ret = -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001085 goto fail;
1086 }
1087
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001088 /* ENDPT UNLOCK */
1089 pthread_mutex_lock(&server_opts.endpts[idx].endpt_lock);
1090
1091 /* READ UNLOCK */
Michal Vasko3031aae2016-01-27 16:07:18 +01001092 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1093
Michal Vaskob48aa812016-01-18 14:13:09 +01001094 /* assign new SID atomically */
1095 /* LOCK */
1096 pthread_spin_lock(&server_opts.sid_lock);
1097 (*session)->id = server_opts.new_session_id++;
1098 /* UNLOCK */
1099 pthread_spin_unlock(&server_opts.sid_lock);
1100
Michal Vasko9e036d52016-01-08 10:49:26 +01001101 /* NETCONF handshake */
Michal Vasko1a38c862016-01-15 15:50:07 +01001102 if (nc_handshake(*session)) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001103 nc_session_free(*session);
1104 *session = NULL;
1105 return -1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001106 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001107 (*session)->status = NC_STATUS_RUNNING;
Michal Vasko9e036d52016-01-08 10:49:26 +01001108
Michal Vasko1a38c862016-01-15 15:50:07 +01001109 return 1;
Michal Vasko9e036d52016-01-08 10:49:26 +01001110
1111fail:
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001112 /* ENDPT UNLOCK */
1113 pthread_mutex_lock(&server_opts.endpts[idx].endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001114 /* WRITE UNLOCK */
1115 pthread_rwlock_unlock(&server_opts.endpt_array_lock);
1116
Michal Vasko1a38c862016-01-15 15:50:07 +01001117 nc_session_free(*session);
1118 *session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001119 return ret;
Michal Vasko9e036d52016-01-08 10:49:26 +01001120}
1121
Michal Vasko3031aae2016-01-27 16:07:18 +01001122int
Michal Vaskob05053d2016-01-22 16:12:06 +01001123nc_connect_callhome(const char *host, uint16_t port, NC_TRANSPORT_IMPL ti, int timeout, struct nc_session **session)
1124{
1125 int sock, ret;
1126
Michal Vaskoc61c4492016-01-25 11:13:34 +01001127 if (!host || !port || !ti || !session) {
1128 ERRARG;
1129 return -1;
1130 }
1131
Michal Vaskob05053d2016-01-22 16:12:06 +01001132 sock = nc_sock_connect(host, port);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001133 if (sock < 0) {
1134 return -1;
Michal Vaskob05053d2016-01-22 16:12:06 +01001135 }
1136
1137 *session = calloc(1, sizeof **session);
1138 if (!(*session)) {
1139 ERRMEM;
1140 close(sock);
1141 return -1;
1142 }
1143 (*session)->status = NC_STATUS_STARTING;
1144 (*session)->side = NC_SERVER;
1145 (*session)->ctx = server_opts.ctx;
1146 (*session)->flags = NC_SESSION_SHAREDCTX | NC_SESSION_CALLHOME;
1147 nc_ctx_lock(-1, NULL);
1148 (*session)->host = lydict_insert(server_opts.ctx, host, 0);
1149 nc_ctx_unlock();
1150 (*session)->port = port;
1151
1152 /* transport lock */
1153 (*session)->ti_lock = malloc(sizeof *(*session)->ti_lock);
1154 if (!(*session)->ti_lock) {
1155 ERRMEM;
1156 close(sock);
1157 ret = -1;
1158 goto fail;
1159 }
1160 pthread_mutex_init((*session)->ti_lock, NULL);
1161
1162 /* sock gets assigned to session or closed */
Michal Vasko3d865d22016-01-28 16:00:53 +01001163#ifdef ENABLE_SSH
Michal Vaskob05053d2016-01-22 16:12:06 +01001164 if (ti == NC_TI_LIBSSH) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001165 /* OPTS LOCK */
1166 pthread_mutex_lock(&ssh_ch_opts_lock);
1167
Michal Vasko3031aae2016-01-27 16:07:18 +01001168 (*session)->ti_opts = &ssh_ch_opts;
1169 ret = nc_accept_ssh_session(*session, sock, timeout);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001170 (*session)->ti_opts = NULL;
1171
1172 /* OPTS UNLOCK */
1173 pthread_mutex_unlock(&ssh_ch_opts_lock);
1174
Michal Vaskob05053d2016-01-22 16:12:06 +01001175 if (ret < 1) {
1176 goto fail;
1177 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001178 } else
1179#endif
1180#ifdef ENABLE_TLS
1181 if (ti == NC_TI_OPENSSL) {
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001182 /* OPTS LOCK */
1183 pthread_mutex_lock(&tls_ch_opts_lock);
1184
Michal Vasko3031aae2016-01-27 16:07:18 +01001185 (*session)->ti_opts = &tls_ch_opts;
1186 ret = nc_accept_tls_session(*session, sock, timeout);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +01001187 (*session)->ti_opts = NULL;
1188
1189 /* OPTS UNLOCK */
1190 pthread_mutex_unlock(&tls_ch_opts_lock);
1191
Michal Vaskob05053d2016-01-22 16:12:06 +01001192 if (ret < 1) {
1193 goto fail;
1194 }
Michal Vasko3d865d22016-01-28 16:00:53 +01001195 } else
1196#endif
1197 {
Michal Vaskob05053d2016-01-22 16:12:06 +01001198 ERRINT;
1199 close(sock);
1200 ret = -1;
1201 goto fail;
1202 }
1203
1204 /* assign new SID atomically */
1205 /* LOCK */
1206 pthread_spin_lock(&server_opts.sid_lock);
1207 (*session)->id = server_opts.new_session_id++;
1208 /* UNLOCK */
1209 pthread_spin_unlock(&server_opts.sid_lock);
1210
1211 /* NETCONF handshake */
1212 if (nc_handshake(*session)) {
1213 ret = -1;
1214 goto fail;
1215 }
1216 (*session)->status = NC_STATUS_RUNNING;
1217
1218 return 1;
1219
1220fail:
1221 nc_session_free(*session);
1222 *session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001223 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +01001224}
1225
Michal Vasko9e036d52016-01-08 10:49:26 +01001226#endif /* ENABLE_SSH || ENABLE_TLS */