blob: 9e65f1060eaff4a67968f49b96daf24c2c48eea1 [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>
33
34#include "log_p.h"
35#include "session_p.h"
36#include "session_server.h"
37
38struct nc_server_opts server_opts;
39
40int
41nc_sock_listen(const char *address, uint32_t port)
42{
43 const int optVal = 1;
44 const socklen_t optLen = sizeof(optVal);
45 int is_ipv4, sock;
46 struct sockaddr_storage saddr;
47
48 struct sockaddr_in *saddr4;
49 struct sockaddr_in6 *saddr6;
50
51
52 if (!strchr(address, ':')) {
53 is_ipv4 = 1;
54 } else {
55 is_ipv4 = 0;
56 }
57
58 sock = socket((is_ipv4 ? AF_INET : AF_INET6), SOCK_STREAM, 0);
59 if (sock == -1) {
60 ERR("%s: could not create socket (%s)", __func__, strerror(errno));
61 goto fail;
62 }
63
64 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&optVal, optLen)) {
65 ERR("%s: could not set socket SO_REUSEADDR option (%s)", __func__, strerror(errno));
66 goto fail;
67 }
68
69 bzero(&saddr, sizeof(struct sockaddr_storage));
70 if (is_ipv4) {
71 saddr4 = (struct sockaddr_in *)&saddr;
72
73 saddr4->sin_family = AF_INET;
74 saddr4->sin_port = htons(port);
75
76 if (inet_pton(AF_INET, address, &saddr4->sin_addr) != 1) {
77 ERR("%s: failed to convert IPv4 address \"%s\"", __func__, address);
78 goto fail;
79 }
80
81 if (bind(sock, (struct sockaddr *)saddr4, sizeof(struct sockaddr_in)) == -1) {
82 ERR("%s: could not bind \"%s\" port %d (%s)", __func__, address, port, strerror(errno));
83 goto fail;
84 }
85
86 } else {
87 saddr6 = (struct sockaddr_in6 *)&saddr;
88
89 saddr6->sin6_family = AF_INET6;
90 saddr6->sin6_port = htons(port);
91
92 if (inet_pton(AF_INET6, address, &saddr6->sin6_addr) != 1) {
93 ERR("%s: failed to convert IPv6 address \"%s\"", __func__, address);
94 goto fail;
95 }
96
97 if (bind(sock, (struct sockaddr *)saddr6, sizeof(struct sockaddr_in6)) == -1) {
98 ERR("%s: could not bind \"%s\" port %d (%s)", __func__, address, port, strerror(errno));
99 goto fail;
100 }
101 }
102
103 if (listen(sock, 5) == -1) {
104 ERR("%s: unable to start listening on \"%s\" port %d (%s)", __func__, address, port, strerror(errno));
105 goto fail;
106 }
107
108 return sock;
109
110fail:
111 if (sock > -1) {
112 close(sock);
113 }
114
115 return -1;
116}
117
118int
Michal Vasko9e036d52016-01-08 10:49:26 +0100119nc_sock_accept(struct nc_bind *binds, uint16_t bind_count, int timeout, NC_TRANSPORT_IMPL *ti, char **host, uint16_t *port)
Michal Vasko086311b2016-01-08 09:53:11 +0100120{
121 uint16_t i;
122 struct pollfd *pfd;
123 struct sockaddr_storage saddr;
124 socklen_t saddr_len = sizeof(saddr);
125 int ret, sock = -1;
126
127 pfd = malloc(bind_count * sizeof *pfd);
128 for (i = 0; i < bind_count; ++i) {
129 pfd[i].fd = binds[i].sock;
130 pfd[i].events = POLLIN;
131 pfd[i].revents = 0;
132 }
133
134 /* poll for a new connection */
135 errno = 0;
136 ret = poll(pfd, bind_count, timeout);
137 if (!ret) {
138 /* we timeouted */
139 free(pfd);
140 return 0;
141 } else if (ret == -1) {
142 ERR("%s: poll failed (%s)", __func__, strerror(errno));
143 free(pfd);
144 return -1;
145 }
146
147 for (i = 0; i < bind_count; ++i) {
148 if (pfd[i].revents & POLLIN) {
149 sock = pfd[i].fd;
150 break;
151 }
152 }
153 free(pfd);
154
155 if (sock == -1) {
156 ERR("%s: fatal error (%s:%d)", __func__, __FILE__, __LINE__);
157 return -1;
158 }
159
160 ret = accept(sock, (struct sockaddr *)&saddr, &saddr_len);
161 if (ret == -1) {
162 ERR("%s: accept failed (%s)", __func__, strerror(errno));
163 return -1;
164 }
165
Michal Vasko9e036d52016-01-08 10:49:26 +0100166 if (ti) {
167 *ti = binds[i].ti;
168 }
169
Michal Vasko086311b2016-01-08 09:53:11 +0100170 /* host was requested */
171 if (host) {
172 if (saddr.ss_family == AF_INET) {
173 *host = malloc(15);
174 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&saddr)->sin_addr.s_addr, *host, 15)) {
175 ERR("%s: inet_ntop failed (%s)", __func__, strerror(errno));
176 free(*host);
177 *host = NULL;
178 }
179
180 if (port) {
181 *port = ntohs(((struct sockaddr_in *)&saddr)->sin_port);
182 }
183 } else if (saddr.ss_family == AF_INET6) {
184 *host = malloc(40);
185 if (!inet_ntop(AF_INET6, ((struct sockaddr_in6 *)&saddr)->sin6_addr.s6_addr, *host, 40)) {
186 ERR("%s: inet_ntop failed (%s)", __func__, strerror(errno));
187 free(*host);
188 *host = NULL;
189 }
190
191 if (port) {
192 *port = ntohs(((struct sockaddr_in6 *)&saddr)->sin6_port);
193 }
194 } else {
195 ERR("%s: source host of an unknown protocol family", __func__);
196 }
197 }
198
199 return ret;
200}
201
202API int
203nc_server_init(struct ly_ctx *ctx)
204{
205 if (!ctx) {
206 ERRARG;
207 return -1;
208 }
209
210 server_opts.ctx = ctx;
211 return 0;
212}
213
214API int
215nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
216{
217 if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)
218 || (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT)))) {
219 ERRARG;
220 return -1;
221 }
222
223 server_opts.wd_basic_mode = basic_mode;
224 server_opts.wd_also_supported = also_supported;
225 return 0;
226}
227
228API int
229nc_server_set_capab_interleave(int interleave_support)
230{
231 if (interleave_support) {
232 server_opts.interleave_capab = 1;
233 } else {
234 server_opts.interleave_capab = 0;
235 }
236
237 return 0;
238}
239
240API int
241nc_server_set_hello_timeout(uint16_t hello_timeout)
242{
243 if (!hello_timeout) {
244 ERRARG;
245 return -1;
246 }
247
248 server_opts.hello_timeout = hello_timeout;
249 return 0;
250}
251
252API int
253nc_server_set_idle_timeout(uint16_t idle_timeout)
254{
255 if (!idle_timeout) {
256 ERRARG;
257 return -1;
258 }
259
260 server_opts.idle_timeout = idle_timeout;
261 return 0;
262}
263
264API int
265nc_server_set_max_sessions(uint16_t max_sessions)
266{
267 server_opts.max_sessions = max_sessions;
268 return 0;
269}
270
271API struct nc_session *
272nc_accept_inout(int fdin, int fdout, const char *username)
273{
274 struct nc_session *session = NULL;
275
276 if (fdin < 0 || fdout < 0 || !username) {
277 ERR("%s: Invalid parameter", __func__);
278 return NULL;
279 }
280
281 if (!server_opts.ctx) {
282 return NULL;
283 }
284
285 /* prepare session structure */
286 session = calloc(1, sizeof *session);
287 if (!session) {
288 ERRMEM;
289 return NULL;
290 }
291 session->status = NC_STATUS_STARTING;
292 session->side = NC_SERVER;
293
294 /* transport specific data */
295 session->ti_type = NC_TI_FD;
296 session->ti.fd.in = fdin;
297 session->ti.fd.out = fdout;
298
299 /* assign context (dicionary needed for handshake) */
300 session->flags = NC_SESSION_SHAREDCTX;
301 session->ctx = server_opts.ctx;
302
303 /* NETCONF handshake */
304 if (nc_handshake(session)) {
305 goto fail;
306 }
307 session->status = NC_STATUS_RUNNING;
308
309 return session;
310
311fail:
312 nc_session_free(session);
313 return NULL;
314}
Michal Vasko9e036d52016-01-08 10:49:26 +0100315
316#if defined(ENABLE_SSH) || defined(ENABLE_TLS)
317
318API int
319nc_server_add_bind_listen(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
320{
321 int sock;
322
323 if (!address || !port || ((ti != NC_TI_LIBSSH) && (ti != NC_TI_OPENSSL))) {
324 ERRARG;
325 return -1;
326 }
327
328 sock = nc_sock_listen(address, port);
329 if (sock == -1) {
330 return -1;
331 }
332
333 ++server_opts.bind_count;
334 server_opts.binds = realloc(server_opts.binds, server_opts.bind_count * sizeof *server_opts.binds);
335
336 server_opts.binds[server_opts.bind_count - 1].address = strdup(address);
337 server_opts.binds[server_opts.bind_count - 1].port = port;
338 server_opts.binds[server_opts.bind_count - 1].sock = sock;
339 server_opts.binds[server_opts.bind_count - 1].ti = ti;
340
341 return 0;
342}
343
344API int
345nc_server_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti)
346{
347 uint32_t i;
348 int ret = -1;
349
350 for (i = 0; i < server_opts.bind_count; ++i) {
351 if ((!address || !strcmp(server_opts.binds[i].address, address))
352 && (!port || (server_opts.binds[i].port == port))
353 && (!ti || (server_opts.binds[i].ti == ti))) {
354 close(server_opts.binds[i].sock);
355 free(server_opts.binds[i].address);
356
357 --server_opts.bind_count;
358 memmove(&server_opts.binds[i], &server_opts.binds[i + 1], (server_opts.bind_count - i) * sizeof *server_opts.binds);
359
360 ret = 0;
361 }
362 }
363
364 return ret;
365}
366
367API void
368nc_server_destroy_binds(void)
369{
370 uint32_t i;
371
372 for (i = 0; i < server_opts.bind_count; ++i) {
373 close(server_opts.binds[i].sock);
374 free(server_opts.binds[i].address);
375 }
376 free(server_opts.binds);
377}
378
379API struct nc_session *
380nc_accept(int timeout)
381{
382 NC_TRANSPORT_IMPL ti;
383 int sock;
384 char *host;
385 uint16_t port;
386 struct nc_session *session = NULL;
387
388 if (!server_opts.ctx || !server_opts.binds) {
389 ERRARG;
390 return NULL;
391 }
392
393 sock = nc_sock_accept(server_opts.binds, server_opts.bind_count, timeout, &ti, &host, &port);
394 if (sock == -1) {
395 return NULL;
396 }
397
398 session = calloc(1, sizeof *session);
399 if (!session) {
400 ERRMEM;
401 goto fail;
402 }
403 session->status = NC_STATUS_STARTING;
404 session->side = NC_SERVER;
405 session->ctx = server_opts.ctx;
406 session->flags = NC_SESSION_SHAREDCTX;
407 session->host = lydict_insert_zc(session->ctx, host);
408 session->port = port;
409
410 /* transport lock */
411 session->ti_lock = malloc(sizeof *session->ti_lock);
412 if (!session->ti_lock) {
413 ERRMEM;
414 goto fail;
415 }
416 pthread_mutex_init(session->ti_lock, NULL);
417
418 if (ti == NC_TI_LIBSSH) {
419 if (nc_accept_ssh_session(session, sock, timeout)) {
420 goto fail;
421 }
422 } else if (ti == NC_TI_OPENSSL) {
423 if (nc_accept_tls_session(session, sock, timeout)) {
424 goto fail;
425 }
426 } else {
427 ERRINT;
428 goto fail;
429 }
430
431 /* NETCONF handshake */
432 if (nc_handshake(session)) {
433 goto fail;
434 }
435 session->status = NC_STATUS_RUNNING;
436
437 return session;
438
439fail:
440 if (sock > -1) {
441 close(sock);
442 }
443 nc_session_free(session);
444
445 return NULL;
446}
447
448#endif /* ENABLE_SSH || ENABLE_TLS */