Skip to content

Commit 3f153fb

Browse files
committed
session client UPDATE support custom capabilities
1 parent 9b26e19 commit 3f153fb

File tree

7 files changed

+224
-138
lines changed

7 files changed

+224
-138
lines changed

src/log_p.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ extern ATOMIC_T verbose_level;
6767
#define NC_CHECK_SRV_INIT_RET(RET) if (!ATOMIC_LOAD_RELAXED(server_opts.new_session_id)) {ERRINITSRV; return (RET);}
6868
#define NC_CHECK_ERRMEM_RET(COND, RET) if ((COND)) {ERRMEM; return (RET);}
6969
#define NC_CHECK_ERRMEM_GOTO(COND, RET, GOTO) if ((COND)) {ERRMEM; RET; goto GOTO;}
70+
#define NC_CHECK_GOTO(COND, GOTO) if ((COND)) {goto GOTO;}
7071

7172
#define GETMACRO1(_1, NAME, ...) NAME
7273
#define GETMACRO2(_1, _2, NAME, ...) NAME

src/session.c

Lines changed: 103 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1079,41 +1079,49 @@ nc_session_free(struct nc_session *session, void (*data_free)(void *))
10791079
free(session);
10801080
}
10811081

1082-
static void
1083-
add_cpblt(const char *capab, char ***cpblts, int *size, int *count)
1082+
/**
1083+
* @brief Add a capability into an array.
1084+
*
1085+
* @param[in] capab Capability to add.
1086+
* @param[in,out] cpblts Array of capabilities to add to, terminated with NULL.
1087+
* @param[in,out] count Count of @p cpblts.
1088+
* @return 0 on success;
1089+
* @return -1 on error.
1090+
*/
1091+
static int
1092+
nc_add_cpblt(const char *capab, char ***cpblts, uint32_t *count)
10841093
{
1085-
size_t len;
1086-
int i;
1094+
uint32_t i, len;
10871095
char *p;
10881096

1089-
if (capab) {
1090-
/* check if already present */
1091-
p = strchr(capab, '?');
1092-
if (p) {
1093-
len = p - capab;
1094-
} else {
1095-
len = strlen(capab);
1096-
}
1097-
for (i = 0; i < *count; i++) {
1098-
if (!strncmp((*cpblts)[i], capab, len) && (((*cpblts)[i][len] == '\0') || ((*cpblts)[i][len] == '?'))) {
1099-
/* already present, do not duplicate it */
1100-
return;
1101-
}
1097+
/* check if already present */
1098+
p = strchr(capab, '?');
1099+
if (p) {
1100+
len = p - capab;
1101+
} else {
1102+
len = strlen(capab);
1103+
}
1104+
for (i = 0; i < *count; i++) {
1105+
if (!strncmp((*cpblts)[i], capab, len) && (((*cpblts)[i][len] == '\0') || ((*cpblts)[i][len] == '?'))) {
1106+
/* already present, do not duplicate it */
1107+
return 0;
11021108
}
11031109
}
11041110

11051111
/* add another capability */
1106-
if (*count == *size) {
1107-
*size += 5;
1108-
*cpblts = nc_realloc(*cpblts, *size * sizeof **cpblts);
1109-
if (!(*cpblts)) {
1110-
ERRMEM;
1111-
return;
1112-
}
1112+
*cpblts = nc_realloc(*cpblts, (*count + 2) * sizeof **cpblts);
1113+
if (!(*cpblts)) {
1114+
ERRMEM;
1115+
return -1;
11131116
}
11141117

1115-
(*cpblts)[*count] = capab ? strdup(capab) : NULL;
1118+
(*cpblts)[*count] = strdup(capab);
11161119
++(*count);
1120+
1121+
/* terminating NULL */
1122+
(*cpblts)[*count] = NULL;
1123+
1124+
return 0;
11171125
}
11181126

11191127
/**
@@ -1122,63 +1130,63 @@ add_cpblt(const char *capab, char ***cpblts, int *size, int *count)
11221130
* @param[in] ctx libyang context.
11231131
* @param[in] version YANG version of the schemas to be included in result.
11241132
* @param[in] config_locked Whether the configuration lock is already held or should be acquired.
1125-
* @return Array of capabilities, NULL on error.
1133+
* @return Array of capabilities terminated with NULL, NULL on error.
11261134
*/
11271135
static char **
11281136
_nc_server_get_cpblts_version(const struct ly_ctx *ctx, LYS_VERSION version, int config_locked)
11291137
{
11301138
char **cpblts;
11311139
const struct lys_module *mod;
11321140
const char *feat;
1133-
int size = 10, count, features_count = 0, dev_count = 0, str_len, len;
1134-
uint32_t u;
1141+
int features_count = 0, dev_count = 0, str_len, len;
1142+
uint32_t i, count;
11351143
LY_ARRAY_COUNT_TYPE v;
11361144
char *yl_content_id;
1137-
uint32_t wd_also_supported;
1138-
uint32_t wd_basic_mode;
1145+
uint32_t wd_also_supported, wd_basic_mode;
11391146

11401147
#define NC_CPBLT_BUF_LEN 4096
11411148
char str[NC_CPBLT_BUF_LEN];
11421149

11431150
NC_CHECK_ARG_RET(NULL, ctx, NULL);
11441151

1145-
cpblts = malloc(size * sizeof *cpblts);
1152+
cpblts = malloc(3 * sizeof *cpblts);
11461153
NC_CHECK_ERRMEM_GOTO(!cpblts, , error);
11471154
cpblts[0] = strdup("urn:ietf:params:netconf:base:1.0");
11481155
cpblts[1] = strdup("urn:ietf:params:netconf:base:1.1");
1156+
cpblts[2] = NULL;
11491157
count = 2;
11501158

11511159
mod = ly_ctx_get_module_implemented(ctx, "ietf-netconf");
11521160
if (mod) {
11531161
if (lys_feature_value(mod, "writable-running") == LY_SUCCESS) {
1154-
add_cpblt("urn:ietf:params:netconf:capability:writable-running:1.0", &cpblts, &size, &count);
1162+
NC_CHECK_GOTO(nc_add_cpblt("urn:ietf:params:netconf:capability:writable-running:1.0", &cpblts, &count), error);
11551163
}
11561164
if (lys_feature_value(mod, "candidate") == LY_SUCCESS) {
1157-
add_cpblt("urn:ietf:params:netconf:capability:candidate:1.0", &cpblts, &size, &count);
1165+
NC_CHECK_GOTO(nc_add_cpblt("urn:ietf:params:netconf:capability:candidate:1.0", &cpblts, &count), error);
11581166
if (lys_feature_value(mod, "confirmed-commit") == LY_SUCCESS) {
1159-
add_cpblt("urn:ietf:params:netconf:capability:confirmed-commit:1.1", &cpblts, &size, &count);
1167+
NC_CHECK_GOTO(nc_add_cpblt("urn:ietf:params:netconf:capability:confirmed-commit:1.1", &cpblts, &count), error);
11601168
}
11611169
}
11621170
if (lys_feature_value(mod, "rollback-on-error") == LY_SUCCESS) {
1163-
add_cpblt("urn:ietf:params:netconf:capability:rollback-on-error:1.0", &cpblts, &size, &count);
1171+
NC_CHECK_GOTO(nc_add_cpblt("urn:ietf:params:netconf:capability:rollback-on-error:1.0", &cpblts, &count), error);
11641172
}
11651173
if (lys_feature_value(mod, "validate") == LY_SUCCESS) {
1166-
add_cpblt("urn:ietf:params:netconf:capability:validate:1.1", &cpblts, &size, &count);
1174+
NC_CHECK_GOTO(nc_add_cpblt("urn:ietf:params:netconf:capability:validate:1.1", &cpblts, &count), error);
11671175
}
11681176
if (lys_feature_value(mod, "startup") == LY_SUCCESS) {
1169-
add_cpblt("urn:ietf:params:netconf:capability:startup:1.0", &cpblts, &size, &count);
1177+
NC_CHECK_GOTO(nc_add_cpblt("urn:ietf:params:netconf:capability:startup:1.0", &cpblts, &count), error);
11701178
}
11711179

11721180
/* The URL capability must be set manually using nc_server_set_capability()
11731181
* because of the need for supported protocols to be included.
11741182
* https://tools.ietf.org/html/rfc6241#section-8.8.3
11751183
*/
11761184
// if (lys_feature_value(mod, "url") == LY_SUCCESS) {
1177-
// add_cpblt("urn:ietf:params:netconf:capability:url:1.0", &cpblts, &size, &count);
1185+
// NC_CHECK_GOTO(nc_add_cpblt("urn:ietf:params:netconf:capability:url:1.0", &cpblts, &count), error);
11781186
// }
11791187

11801188
if (lys_feature_value(mod, "xpath") == LY_SUCCESS) {
1181-
add_cpblt("urn:ietf:params:netconf:capability:xpath:1.0", &cpblts, &size, &count);
1189+
NC_CHECK_GOTO(nc_add_cpblt("urn:ietf:params:netconf:capability:xpath:1.0", &cpblts, &count), error);
11821190
}
11831191
}
11841192

@@ -1227,19 +1235,19 @@ _nc_server_get_cpblts_version(const struct ly_ctx *ctx, LYS_VERSION version, int
12271235
}
12281236
str[strlen(str) - 1] = '\0';
12291237

1230-
add_cpblt(str, &cpblts, &size, &count);
1238+
NC_CHECK_GOTO(nc_add_cpblt(str, &cpblts, &count), error);
12311239
}
12321240
}
12331241
}
12341242

12351243
/* other capabilities */
1236-
for (u = 0; u < server_opts.capabilities_count; u++) {
1237-
add_cpblt(server_opts.capabilities[u], &cpblts, &size, &count);
1244+
for (i = 0; i < server_opts.capabilities_count; i++) {
1245+
NC_CHECK_GOTO(nc_add_cpblt(server_opts.capabilities[i], &cpblts, &count), error);
12381246
}
12391247

12401248
/* models */
1241-
u = 0;
1242-
while ((mod = ly_ctx_get_module_iter(ctx, &u))) {
1249+
i = 0;
1250+
while ((mod = ly_ctx_get_module_iter(ctx, &i))) {
12431251
if (nc_server_is_mod_ignored(mod->name, config_locked)) {
12441252
/* ignored, not part of the cababilities */
12451253
continue;
@@ -1265,12 +1273,12 @@ _nc_server_get_cpblts_version(const struct ly_ctx *ctx, LYS_VERSION version, int
12651273
/* new one (capab defined in RFC 8526 section 2) */
12661274
sprintf(str, "urn:ietf:params:netconf:capability:yang-library:1.1?revision=%s&content-id=%s",
12671275
mod->revision, yl_content_id);
1268-
add_cpblt(str, &cpblts, &size, &count);
1276+
NC_CHECK_GOTO(nc_add_cpblt(str, &cpblts, &count), error);
12691277
} else {
12701278
/* old one (capab defined in RFC 7950 section 5.6.4) */
12711279
sprintf(str, "urn:ietf:params:netconf:capability:yang-library:1.0?revision=%s&module-set-id=%s",
12721280
mod->revision, yl_content_id);
1273-
add_cpblt(str, &cpblts, &size, &count);
1281+
NC_CHECK_GOTO(nc_add_cpblt(str, &cpblts, &count), error);
12741282
}
12751283
free(yl_content_id);
12761284
continue;
@@ -1325,23 +1333,25 @@ _nc_server_get_cpblts_version(const struct ly_ctx *ctx, LYS_VERSION version, int
13251333
}
13261334
}
13271335

1328-
add_cpblt(str, &cpblts, &size, &count);
1336+
NC_CHECK_GOTO(nc_add_cpblt(str, &cpblts, &count), error);
13291337
}
13301338

13311339
/* HELLO UNLOCK */
13321340
nc_rwlock_unlock(&server_opts.hello_lock, __func__);
13331341

1334-
/* ending NULL capability */
1335-
add_cpblt(NULL, &cpblts, &size, &count);
1336-
13371342
return cpblts;
13381343

13391344
unlock_error:
13401345
/* HELLO UNLOCK */
13411346
nc_rwlock_unlock(&server_opts.hello_lock, __func__);
13421347

13431348
error:
1344-
free(cpblts);
1349+
if (cpblts) {
1350+
for (i = 0; cpblts[i]; ++i) {
1351+
free(cpblts[i]);
1352+
}
1353+
free(cpblts);
1354+
}
13451355
return NULL;
13461356
}
13471357

@@ -1414,6 +1424,44 @@ parse_cpblts(struct lyd_node *capabilities, char ***list)
14141424
return ver;
14151425
}
14161426

1427+
/**
1428+
* @brief Get client-side capabilities.
1429+
*
1430+
* @return Array of capabilities terminated with NULL, NULL on error.
1431+
*/
1432+
static char **
1433+
nc_client_get_cpblts(void)
1434+
{
1435+
char **cpblts = NULL;
1436+
uint32_t i, count;
1437+
1438+
cpblts = malloc(3 * sizeof *cpblts);
1439+
NC_CHECK_ERRMEM_GOTO(!cpblts, , error);
1440+
1441+
cpblts[0] = strdup("urn:ietf:params:netconf:base:1.0");
1442+
NC_CHECK_ERRMEM_GOTO(!cpblts[0], , error);
1443+
cpblts[1] = strdup("urn:ietf:params:netconf:base:1.1");
1444+
NC_CHECK_ERRMEM_GOTO(!cpblts[1], , error);
1445+
cpblts[2] = NULL;
1446+
count = 3;
1447+
1448+
/* custom capabilities */
1449+
for (i = 0; i < client_opts.capabilities_count; ++i) {
1450+
NC_CHECK_GOTO(nc_add_cpblt(client_opts.capabilities[i], &cpblts, &count), error);
1451+
}
1452+
1453+
return cpblts;
1454+
1455+
error:
1456+
if (cpblts) {
1457+
for (i = 0; cpblts[i]; ++i) {
1458+
free(cpblts[i]);
1459+
}
1460+
free(cpblts);
1461+
}
1462+
return NULL;
1463+
}
1464+
14171465
/**
14181466
* @brief Send NETCONF hello message on a session.
14191467
*
@@ -1431,11 +1479,10 @@ nc_send_hello_io(struct nc_session *session, int config_locked)
14311479

14321480
if (session->side == NC_CLIENT) {
14331481
/* client side hello - send only NETCONF base capabilities */
1434-
cpblts = malloc(3 * sizeof *cpblts);
1435-
NC_CHECK_ERRMEM_RET(!cpblts, NC_MSG_ERROR);
1436-
cpblts[0] = strdup("urn:ietf:params:netconf:base:1.0");
1437-
cpblts[1] = strdup("urn:ietf:params:netconf:base:1.1");
1438-
cpblts[2] = NULL;
1482+
cpblts = nc_client_get_cpblts();
1483+
if (!cpblts) {
1484+
return NC_MSG_ERROR;
1485+
}
14391486

14401487
timeout_io = NC_CLIENT_HELLO_TIMEOUT * 1000;
14411488
sid = NULL;

src/session_client.c

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* @brief libnetconf2 session client functions
55
*
66
* @copyright
7-
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
7+
* Copyright (c) 2015 - 2026 CESNET, z.s.p.o.
88
*
99
* This source code is licensed under BSD 3-Clause License (the "License").
1010
* You may not use this file except in compliance with the License.
@@ -99,6 +99,7 @@ static void
9999
nc_client_context_free(void *ptr)
100100
{
101101
struct nc_client_context *c = (struct nc_client_context *)ptr;
102+
uint32_t i;
102103

103104
if (--(c->refcount)) {
104105
/* still used */
@@ -115,11 +116,13 @@ nc_client_context_free(void *ptr)
115116
{
116117
/* for the main thread the same is done in nc_client_destroy() */
117118
free(c->opts.schema_searchpath);
119+
for (i = 0; i < c->opts.capabilities_count; i++) {
120+
free(c->opts.capabilities[i]);
121+
}
122+
free(c->opts.capabilities);
118123
free(c->unix_opts.username);
119124

120125
#ifdef NC_ENABLED_SSH_TLS
121-
int i;
122-
123126
for (i = 0; i < c->opts.ch_bind_count; ++i) {
124127
close(c->opts.ch_binds[i].sock);
125128
free((char *)c->opts.ch_binds[i].address);
@@ -204,9 +207,6 @@ nc_client_context_location(void)
204207
return e;
205208
}
206209

207-
#define client_opts nc_client_context_location()->opts
208-
#define unix_opts nc_client_context_location()->unix_opts
209-
210210
API void *
211211
nc_client_get_thread_context(void)
212212
{
@@ -325,12 +325,35 @@ nc_client_unix_set_username(const char *username)
325325
NC_CHECK_ERRMEM_RET(!new_user, -1);
326326
}
327327

328-
free(unix_opts.username);
329-
unix_opts.username = new_user;
328+
free(client_unix_opts.username);
329+
client_unix_opts.username = new_user;
330330

331331
return 0;
332332
}
333333

334+
API int
335+
nc_client_set_capability(const char *value)
336+
{
337+
int rc = 0;
338+
void *mem;
339+
340+
if (!value || !value[0]) {
341+
ERRARG(NULL, "value must not be empty");
342+
return -1;
343+
}
344+
345+
mem = realloc(client_opts.capabilities, (client_opts.capabilities_count + 1) * sizeof *client_opts.capabilities);
346+
NC_CHECK_ERRMEM_GOTO(!mem, rc = -1, cleanup);
347+
client_opts.capabilities = mem;
348+
349+
client_opts.capabilities[client_opts.capabilities_count] = strdup(value);
350+
NC_CHECK_ERRMEM_GOTO(!client_opts.capabilities[client_opts.capabilities_count], rc = -1, cleanup);
351+
client_opts.capabilities_count++;
352+
353+
cleanup:
354+
return rc;
355+
}
356+
334357
struct module_info {
335358
char *name;
336359
char *revision;
@@ -1485,8 +1508,8 @@ nc_connect_unix(const char *address, struct ly_ctx *ctx)
14851508
}
14861509

14871510
/* NETCONF username */
1488-
if (unix_opts.username) {
1489-
username = strdup(unix_opts.username);
1511+
if (client_unix_opts.username) {
1512+
username = strdup(client_unix_opts.username);
14901513
} else {
14911514
pw = nc_getpw(geteuid(), NULL, &pw_buf, &buf, &buf_size);
14921515
if (!pw) {

0 commit comments

Comments
 (0)