6a0fea6ef00223cb1705dd3aaca9b0afea7092f2
1 /*
2 * libJUNOS - src/junos.c
3 * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
28 /*
29 * Base object used to manage the connection to a JUNOS device.
30 */
32 #include "junos.h"
34 #include "libjunos_features.h"
36 #include <errno.h>
38 #include <stdlib.h>
39 #include <string.h>
41 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <netdb.h>
45 #include <libssh2.h>
47 #include <libxml/tree.h>
48 #include <libxml/parser.h>
50 #ifndef LIBXML_PUSH_ENABLED
51 # error "libxml has not been compiled with push parser support"
52 #endif
54 /*
55 * private data structures
56 */
58 struct junos {
59 char *hostname;
60 char *username;
61 char *password;
63 void *access;
65 xmlParserCtxtPtr xml_ctx;
67 junos_error_t err;
68 };
70 /*
71 * private helper functions
72 */
74 static ssize_t
75 read_lines(junos_t *junos, char *buf, size_t buf_len)
76 {
77 ssize_t count = 0;
79 while (42) {
80 ssize_t status;
82 /* junos_ssh_recv requires at least two bytes */
83 if (buf_len - 2 < (size_t)count) {
84 dprintf("Receive buffer too small\n");
85 break;
86 }
88 status = junos_ssh_recv(junos->access,
89 buf + count, buf_len - (size_t)count);
90 if (status < 0) {
91 count = -1;
92 break;
93 }
95 if (! status)
96 if (count)
97 break;
98 /* else: retry */
100 count += status;
102 if (buf[count - 1] == '\n')
103 break;
104 }
105 return count;
106 } /* read_line */
108 /*
109 * public API
110 */
112 int
113 junos_init(void)
114 {
115 int status;
117 status = libssh2_init(/* flags = */ 0);
118 if (status < 0) {
119 dprintf("Failed to initialize libssh2 (status %d)\n", status);
120 return status;
121 }
123 LIBXML_TEST_VERSION;
124 return 0;
125 } /* junos_init */
127 junos_t *
128 junos_new(char *hostname, char *username, char *password)
129 {
130 junos_t *junos;
132 if ((! hostname) || (! username))
133 return NULL;
135 junos = calloc(1, sizeof(*junos));
136 if (! junos)
137 return NULL;
139 junos->hostname = strdup(hostname);
140 junos->username = strdup(username);
141 if (password)
142 junos->password = strdup(password);
144 if ((! junos->hostname) || (! junos->username)
145 || (password && (! junos->password))) {
146 junos_free(junos);
147 return NULL;
148 }
150 junos->access = NULL;
151 junos->xml_ctx = NULL;
153 junos_clear_error(junos);
154 return junos;
155 } /* junos_new */
157 char *
158 junos_get_hostname(junos_t *junos)
159 {
160 if (! junos)
161 return NULL;
162 return junos->hostname;
163 } /* junos_get_hostname */
165 char *
166 junos_get_username(junos_t *junos)
167 {
168 if (! junos)
169 return NULL;
170 return junos->username;
171 } /* junos_get_username */
173 char *
174 junos_get_password(junos_t *junos)
175 {
176 if (! junos)
177 return NULL;
178 return junos->password;
179 } /* junos_get_password */
181 void
182 junos_free(junos_t *junos)
183 {
184 if (! junos)
185 return;
187 junos_disconnect(junos);
189 if (junos->hostname)
190 free(junos->hostname);
191 if (junos->username)
192 free(junos->username);
193 if (junos->password)
194 free(junos->password);
196 free(junos);
197 } /* junos_free */
199 int
200 junos_connect(junos_t *junos)
201 {
202 char recv_buf[4096];
203 ssize_t count = 0;
204 ssize_t status;
206 char *tmp;
208 char js_handshake[] = "<?xml version=\"1.0\" encoding=\"us-ascii\"?>"
209 "<junoscript version=\"1.0\" os=\"libJUNOS\">";
211 if (! junos)
212 return -1;
214 junos->access = junos_ssh_new(junos);
215 if (! junos->access)
216 return -1;
218 if (junos_ssh_connect(junos->access))
219 return -1;
221 while (42) {
222 status = read_lines(junos, recv_buf + count,
223 sizeof(recv_buf) - (size_t)count);
224 if (status < 0)
225 break;
227 count += status;
229 if ((tmp = strstr(recv_buf, "<?xml"))
230 && strstr(tmp, "<junoscript"))
231 break;
232 }
234 dprintf("Header: %s", recv_buf);
236 /* don't send the trailing null byte */
237 status = junos_ssh_send(junos->access,
238 js_handshake, sizeof(js_handshake) - 1);
239 if (status != (ssize_t)sizeof(js_handshake) - 1) {
240 dprintf("Failed to send JUNOScript handshake (status %d)\n",
241 (int)status);
242 return -1;
243 }
245 read_lines(junos, recv_buf, sizeof(recv_buf));
246 dprintf(" -> %s", recv_buf);
247 return 0;
248 } /* junos_connect */
250 int
251 junos_disconnect(junos_t *junos)
252 {
253 if (! junos)
254 return -1;
256 if (junos->access)
257 junos_ssh_free(junos->access);
258 junos->access = NULL;
260 return 0;
261 } /* junos_disconnect */
263 xmlDocPtr
264 junos_simple_command(junos_t *junos, const char *cmd)
265 {
266 char cmd_string[1024];
267 char recv_buf[4096];
268 ssize_t status;
270 int xml_status;
272 xmlDocPtr doc;
274 if ((! junos) || (! cmd)) {
275 junos_set_error(junos, JUNOS_SYS_ERROR, EINVAL,
276 "junos_simple_command() requires the "
277 "'junos' and 'cmd' arguments");
278 return NULL;
279 }
281 if (! junos->access) {
282 junos_set_error(junos, JUNOS_SYS_ERROR, EINVAL,
283 "Please call junos_connect() before submitting commands");
284 return NULL;
285 }
287 snprintf(cmd_string, sizeof(cmd_string),
288 "<rpc><%s/></rpc>", cmd);
289 status = junos_ssh_send(junos->access, cmd_string, strlen(cmd_string));
290 if (status != (ssize_t)strlen(cmd_string)) {
291 dprintf("Failed to send cmd '%s' (status %d)\n",
292 cmd_string, (int)status);
293 return NULL;
294 }
296 errno = 0;
297 junos->xml_ctx = xmlCreatePushParserCtxt(/* sax = */ NULL,
298 /* user_data = */ NULL,
299 /* chunk = */ NULL, /* size = */ 0,
300 /* filename = */ NULL);
301 if (! junos->xml_ctx) {
302 junos_set_error(junos, JUNOS_SYS_ERROR, errno,
303 "Failed to create XML parser context");
304 return NULL;
305 }
307 while (42) {
308 status = read_lines(junos, recv_buf, sizeof(recv_buf));
309 if (status < 0)
310 break;
312 dprintf(" -> %s", recv_buf);
314 xml_status = xmlParseChunk(junos->xml_ctx, recv_buf, (int)status,
315 /* terminate = */ 0);
316 if (xml_status) {
317 junos_set_error(junos, JUNOS_XML_ERROR, xml_status,
318 "XML parsing failed");
319 break;
320 }
322 if (strstr(recv_buf, "</rpc-reply>"))
323 break;
324 }
326 /* finish parser */
327 xmlParseChunk(junos->xml_ctx, "", 0, /* terminate = */ 1);
329 doc = junos->xml_ctx->myDoc;
330 if (xml_status || (! junos->xml_ctx->wellFormed)) {
331 if ((! xml_status) && (! status))
332 junos_set_error(junos, JUNOS_XML_ERROR, -1,
333 "XML validation failed");
334 if (status >= 0)
335 status = -1;
336 }
338 xmlFreeParserCtxt(junos->xml_ctx);
339 junos->xml_ctx = NULL;
341 if (status < 0) {
342 xmlFreeDoc(doc);
343 return NULL;
344 }
346 return doc;
347 } /* junos_simple_command */
349 /* error handling */
351 const char *
352 junos_get_errstr(junos_t *junos)
353 {
354 if (! junos)
355 return NULL;
356 return junos->err.errmsg;
357 } /* junos_get_errstr */
359 void
360 junos_clear_error(junos_t *junos)
361 {
362 junos_error_t no_error = JUNOS_NO_ERROR;
364 if (! junos)
365 return;
367 junos->err = no_error;
368 } /* junos_clear_error */
370 int
371 junos_set_error(junos_t *junos, int type, int error,
372 char *msg_prefix, ...)
373 {
374 va_list ap;
375 int status;
377 va_start(ap, msg_prefix);
378 status = junos_set_verror(junos, type, error, msg_prefix, ap);
379 va_end(ap);
381 return status;
382 } /* junos_set_error */
384 int
385 junos_set_verror(junos_t *junos, int type, int error,
386 char *msg_prefix, va_list ap)
387 {
388 junos_error_t *err;
390 char errbuf[1024];
391 const char *err_msg;
393 char prefix[1024];
395 int status = 0;
397 if (! junos)
398 return -1;
400 err = &junos->err;
402 err->type = type;
403 err->error = error;
405 vsnprintf(prefix, sizeof(prefix), msg_prefix, ap);
406 prefix[sizeof(prefix) - 1] = '\0';
408 switch (type) {
409 case JUNOS_OK:
410 snprintf(err->errmsg, sizeof(err->errmsg),
411 "i%s: success", prefix);
412 break;
413 case JUNOS_SYS_ERROR:
414 {
415 int failed = 0;
417 #if STRERROR_R_CHAR_P
418 errbuf[0] = '\0';
419 err_msg = strerror_r(error, errbuf, sizeof(errbuf));
420 if (! err_msg)
421 err_msg = errbuf;
422 if (! err_msg[0])
423 failed = 1;
424 #else /* STRERROR_R_CHAR_P */
425 failed = strerror_r(error, errbuf, sizeof(errbuf));
426 err_msg = errbuf;
427 #endif /* STRERROR_R_CHAR_P */
429 if (failed)
430 snprintf(err->errmsg, sizeof(err->errmsg),
431 "%s: system error #%i", prefix, error);
432 else
433 snprintf(err->errmsg, sizeof(err->errmsg),
434 "%s: %s", prefix, err_msg);
435 }
436 break;
437 case JUNOS_GAI_ERROR:
438 if (error == EAI_SYSTEM)
439 return junos_set_error(junos, JUNOS_SYS_ERROR, error,
440 "%s: network address translation failed", prefix);
442 err_msg = gai_strerror(error);
443 if (err_msg)
444 snprintf(err->errmsg, sizeof(err->errmsg),
445 "%s: %s", prefix, err_msg);
446 else
447 snprintf(err->errmsg, sizeof(err->errmsg),
448 "%s: network address translation error #%i",
449 prefix, error);
450 break;
451 case JUNOS_XML_ERROR:
452 {
453 xmlErrorPtr xml_err;
455 if (! junos->xml_ctx) /* don't touch any error information */
456 return 0;
458 xml_err = xmlCtxtGetLastError(junos->xml_ctx);
459 if (! xml_err)
460 return 0;
462 err->error = xml_err->code;
463 snprintf(err->errmsg, sizeof(err->errmsg),
464 "%s: %s", prefix, xml_err->message);
465 }
466 break;
467 case JUNOS_ACCESS_ERROR:
468 status = junos_set_ssh_error(err, junos->access,
469 "%s", prefix);
470 break;
471 default:
472 return -1;
473 break;
474 }
476 err->errmsg[sizeof(err->errmsg) - 1] = '\0';
477 dprintf("ERROR: %s\n", err->errmsg);
479 return status;
480 } /* junos_set_verror */
482 /* features */
484 unsigned int
485 libjunos_version(void)
486 {
487 return LIBJUNOS_VERSION;
488 } /* libjunos_version */
490 const char *
491 libjunos_version_string(void)
492 {
493 return LIBJUNOS_VERSION_STRING;
494 } /* libjunos_version_string */
496 const char *
497 libjunos_version_extra(void)
498 {
499 return LIBJUNOS_VERSION_EXTRA;
500 } /* libjunos_version_extra */
502 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */