1 /*
2 * radd, radtool, and sratool common client code
3 *
4 * Copyright (c) 2014-2018 by Farsight Security, Inc.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 #include <axa/client.h>
20 #include <axa/socket.h>
21 #include <axa/strbuf.h>
22 #include <axa/json.h>
23 #include <axa/yajl_shortcuts.h>
24
25 #include <libmy/ubuf-pb.h> /* query protobuf version */
26
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <signal.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #ifdef __linux
33 #include <bsd/string.h> /* for strlcpy() */
34 #endif
35 #include <sysexits.h>
36 #include <sys/utsname.h>
37 #include <unistd.h>
38 #include <wdns.h>
39
40 #include <yajl/yajl_version.h>
41 #include <yajl/yajl_gen.h>
42
43 #define MIN_BACKOFF_MS (1*1000)
44 #define MAX_BACKOFF_MS (60*1000)
45
46 #ifndef HOST_NAME_MAX
47 #define HOST_NAME_MAX 255
48 #endif
49
50 void
axa_client_init(axa_client_t * client)51 axa_client_init(axa_client_t *client)
52 {
53 time_t backoff;
54 struct timeval retry;
55
56 /* Do not change the back-off clock. */
57 backoff = client->backoff;
58 retry = client->retry;
59
60 memset(client, 0, sizeof(*client));
61 axa_io_init(&client->io);
62
63 client->retry = retry;
64 client->backoff = backoff;
65 }
66
67 void
axa_client_backoff(axa_client_t * client)68 axa_client_backoff(axa_client_t *client)
69 {
70 axa_client_close(client);
71
72 gettimeofday(&client->retry, NULL);
73 client->backoff = max(MIN_BACKOFF_MS, client->backoff*2);
74 if (client->backoff > MAX_BACKOFF_MS)
75 client->backoff = MAX_BACKOFF_MS;
76 }
77
78 void
axa_client_backoff_max(axa_client_t * client)79 axa_client_backoff_max(axa_client_t *client)
80 {
81 axa_client_close(client);
82
83 gettimeofday(&client->retry, NULL);
84 client->backoff = MAX_BACKOFF_MS;
85 }
86
87 void
axa_client_backoff_reset(axa_client_t * client)88 axa_client_backoff_reset(axa_client_t *client)
89 {
90 client->retry.tv_sec = 0;
91 client->backoff = 0;
92 }
93
94 time_t /* ms until retry or < 0 */
axa_client_again(axa_client_t * client,struct timeval * now)95 axa_client_again(axa_client_t *client, struct timeval *now)
96 {
97 struct timeval tv;
98
99 if (client->retry.tv_sec == 0)
100 return (-1);
101
102 if (now == NULL)
103 now = &tv;
104 gettimeofday(now, NULL);
105
106 return (client->backoff - axa_elapsed_ms(now, &client->retry));
107 }
108
109 void
axa_client_close(axa_client_t * client)110 axa_client_close(axa_client_t *client)
111 {
112 /* Everything except the reconnection timers must be set again
113 * if and when it is re-opened. */
114
115 axa_io_close(&client->io);
116
117 if (client->hello != NULL) {
118 free(client->hello);
119 client->hello = NULL;
120 }
121
122 client->have_id = false;
123 client->clnt_id = 0;
124 }
125
126 static bool
connect_ssh(axa_emsg_t * emsg,axa_client_t * client,bool nonblock,bool ssh_debug)127 connect_ssh(axa_emsg_t *emsg, axa_client_t *client,
128 bool nonblock, bool ssh_debug)
129 {
130 int in_fildes[2], out_fildes[2], err_fildes[2];
131
132 if (0 > pipe(in_fildes)) {
133 axa_pemsg(emsg, "pipe(%s): %s",
134 client->io.label, strerror(errno));
135 return (false);
136 }
137 if (0 > pipe(out_fildes)) {
138 axa_pemsg(emsg, "pipe(%s): %s",
139 client->io.label, strerror(errno));
140 close(in_fildes[0]);
141 close(in_fildes[1]);
142 return (false);
143 }
144 if (0 > pipe(err_fildes)) {
145 axa_pemsg(emsg, "pipe(%s): %s",
146 client->io.label, strerror(errno));
147 close(in_fildes[0]);
148 close(in_fildes[1]);
149 close(out_fildes[0]);
150 close(out_fildes[1]);
151 return (false);
152 }
153 client->io.tun_pid = fork();
154 if (client->io.tun_pid == -1) {
155 axa_pemsg(emsg, "ssh fork(%s): %s",
156 client->io.label, strerror(errno));
157 close(in_fildes[0]);
158 close(in_fildes[1]);
159 close(out_fildes[0]);
160 close(out_fildes[1]);
161 close(err_fildes[0]);
162 close(err_fildes[1]);
163 return (false);
164 }
165 if (client->io.tun_pid == 0) {
166 /* Run ssh in the child process. */
167 signal(SIGPIPE, SIG_IGN);
168 signal(SIGHUP, SIG_IGN);
169 signal(SIGTERM, SIG_IGN);
170 signal(SIGINT, SIG_IGN);
171 #ifdef SIGXFSZ
172 signal(SIGXFSZ, SIG_IGN);
173 #endif
174
175 if (0 > dup2(in_fildes[1], STDOUT_FILENO)
176 || 0 > dup2(out_fildes[0], STDIN_FILENO)
177 || 0 > dup2(err_fildes[1], STDERR_FILENO)) {
178 axa_error_msg("ssh dup2(%s): %s",
179 client->io.label, strerror(errno));
180 exit(EX_OSERR);
181 }
182 close(in_fildes[0]);
183 close(out_fildes[1]);
184 close(err_fildes[0]);
185
186 /*
187 * -v only when debugging is enabled
188 * -T no pseudo-tty
189 * -a disable forwarding of the authentication agent
190 * connection
191 * -x no X11 forwarding
192 * -oBatchMode=yes no interactive passphrase/password querying
193 * -oStrictHostKeyChecking=no do not look for the server's key
194 * in the known_hosts file and so do not worry about
195 * men in the middle to prevent user interaction when
196 * the server's key changes
197 * -oCheckHostIP=no do not check for the server's IP address
198 * in the known_hosts file
199 * -enone for no escape character because we are moving binary
200 */
201 if (ssh_debug)
202 execlp("ssh", "ssh", "-v",
203 "-Tax", "-oBatchMode=yes",
204 "-oStrictHostKeyChecking=no",
205 "-oCheckHostIP=no",
206 "-enone",
207 client->io.addr, NULL);
208 else
209 execlp("ssh", "ssh",
210 "-Tax", "-oBatchMode=yes",
211 "-oStrictHostKeyChecking=no",
212 "-oCheckHostIP=no",
213 "-enone",
214 client->io.addr, NULL);
215 axa_error_msg("exec(ssh %s): %s",
216 client->io.addr, strerror(errno));
217 exit(EX_OSERR);
218 }
219
220 /* Finish setting up links to the ssh child in this the parent. */
221 client->io.i_fd = in_fildes[0];
222 client->io.i_events = AXA_POLL_IN;
223 client->io.o_fd = out_fildes[1];
224 client->io.o_events = 0;
225 client->io.tun_fd = err_fildes[0];
226 close(in_fildes[1]);
227 close(out_fildes[0]);
228 close(err_fildes[1]);
229
230 if (!axa_set_sock(emsg, client->io.i_fd, client->io.label,
231 0, nonblock)
232 || !axa_set_sock(emsg, client->io.o_fd, client->io.label,
233 0, nonblock)
234 || !axa_set_sock(emsg, client->io.tun_fd, client->io.label,
235 0, true)) {
236 return (false);
237 }
238
239 return (true);
240 }
241
242 static axa_connect_result_t
socket_connect(axa_emsg_t * emsg,axa_client_t * client)243 socket_connect(axa_emsg_t *emsg, axa_client_t *client)
244 {
245 int i;
246
247 if (!AXA_CLIENT_OPENED(client)) {
248 client->io.o_fd = socket(client->io.su.sa.sa_family,
249 SOCK_STREAM, 0);
250 if (client->io.o_fd < 0) {
251 axa_pemsg(emsg, "socket(%s): %s",
252 client->io.label, strerror(errno));
253 axa_client_backoff_max(client);
254 return (AXA_CONNECT_ERR);
255 }
256 client->io.i_fd = client->io.o_fd;
257
258 if (!axa_set_sock(emsg, client->io.o_fd, client->io.label,
259 client->io.bufsize, client->io.nonblock)) {
260 axa_client_backoff_max(client);
261 return (AXA_CONNECT_ERR);
262 }
263 }
264
265 if (!client->io.connected_tcp) {
266 i = connect(client->io.o_fd, &client->io.su.sa,
267 AXA_SU_LEN(&client->io.su));
268 if (0 <= i || errno == EISCONN) {
269 /* We finished a new connection or a previously
270 * started non-blocking connection. */
271 client->io.connected_tcp = true;
272 client->io.i_events = AXA_POLL_IN;
273 client->io.o_events = 0;
274
275 } else if (client->io.nonblock && AXA_CONN_WAIT_ERRORS()) {
276 /* Non-blocking connection unfinished. */
277 client->io.i_events = AXA_POLL_OUT;
278 client->io.o_events = 0;
279 return (AXA_CONNECT_INCOM);
280
281 } else {
282 /* Failed to connect. */
283 axa_pemsg(emsg, "connect(%s): %s",
284 client->io.label, strerror(errno));
285 axa_client_backoff(client);
286 return (AXA_CONNECT_TEMP);
287 }
288 }
289
290 return (AXA_CONNECT_DONE);
291 }
292
293 axa_connect_result_t
axa_client_connect(axa_emsg_t * emsg,axa_client_t * client)294 axa_client_connect(axa_emsg_t *emsg, axa_client_t *client)
295 {
296 axa_p_hdr_t hdr;
297 axa_connect_result_t connect_result;
298 uint8_t pvers_save;
299
300 if (AXA_CLIENT_CONNECTED(client))
301 return (AXA_CONNECT_DONE);
302
303 /* Clamp the AXA protocol version to 1 (guaranteed to be spoken by all
304 * AXA clients and servers. This is done just before the identification
305 * and authentication (I&A) message exchange and subsequent spin-up of
306 * the encrypted tunnel. We do this for two reasons:
307 *
308 * 1. A client's preferred AXA protocol version (AXA_PVERS) may be
309 * higher than what the server can understand (such would be the
310 * case when a newer client connects to an older server).
311 * 2. AXA protocol version negotiation is performed during the HELLO
312 * handshake. This happens after I&A, allowing it to be
313 * performed inside an encrypted tunnel and prevent information
314 * leakage to an observer. The issue is the I&A process involves the
315 * exchange of AXA messages (AXA_P_OP_USER and/or AXA_P_OP_NOP). If
316 * the AXA protocol version were too new, the server wouldn't
317 * understand (but also wouldn't have had the option to tell the
318 * client to downgrade its AXA protocol version).
319 *
320 * The downside here is that the AXA I&A message exchange protocol
321 * cannot change. New I&A methods can be added or removed but the
322 * structure of the messages must remain the same.
323 *
324 * After I&A, the AXA protocol may be set to a version the client
325 * prefers, and the HELLO handshake should proceed.
326 */
327 axa_io_pvers_get(&client->io, &pvers_save);
328 axa_io_pvers_set(&client->io, AXA_P_PVERS1);
329
330 switch (client->io.type) {
331 case AXA_IO_TYPE_UNIX:
332 case AXA_IO_TYPE_TCP:
333 connect_result = socket_connect(emsg, client);
334 if (connect_result != AXA_CONNECT_DONE) {
335 axa_io_pvers_set(&client->io, pvers_save);
336 return (connect_result);
337 }
338 client->io.connected = true;
339
340 /* TCP and UNIX domain sockets need a user name */
341 if (client->io.user.name[0] != '\0') {
342 if (!axa_client_send(emsg, client,
343 AXA_TAG_NONE, AXA_P_OP_USER, &hdr,
344 &client->io.user,
345 sizeof(client->io.user))) {
346 axa_client_backoff(client);
347 axa_io_pvers_set(&client->io, pvers_save);
348 return (AXA_CONNECT_ERR);
349 }
350 axa_p_to_str(emsg->c, sizeof(emsg->c),
351 true, &hdr,
352 (axa_p_body_t *)&client->io.user);
353 axa_io_pvers_set(&client->io, pvers_save);
354 return (AXA_CONNECT_USER);
355 }
356 break;
357
358 case AXA_IO_TYPE_SSH:
359 if (!AXA_CLIENT_OPENED(client)) {
360 if (!connect_ssh(emsg, client, client->io.nonblock,
361 client->io.tun_debug)) {
362 axa_client_backoff_max(client);
363 axa_io_pvers_set(&client->io, pvers_save);
364 return (AXA_CONNECT_ERR);
365 }
366 client->io.connected_tcp = true;
367 client->io.connected = true;
368 }
369 break;
370
371 case AXA_IO_TYPE_TLS:
372 connect_result = socket_connect(emsg, client);
373 if (connect_result != AXA_CONNECT_DONE) {
374 axa_io_pvers_set(&client->io, pvers_save);
375 return (connect_result);
376 }
377 switch (axa_tls_start(emsg, &client->io)) {
378 case AXA_IO_OK:
379 break;
380 case AXA_IO_ERR:
381 axa_client_backoff_max(client);
382 axa_io_pvers_set(&client->io, pvers_save);
383 return (AXA_CONNECT_ERR);
384 case AXA_IO_BUSY:
385 AXA_ASSERT(client->io.nonblock);
386 axa_io_pvers_set(&client->io, pvers_save);
387 return (connect_result);
388 case AXA_IO_TUNERR:
389 case AXA_IO_KEEPALIVE:
390 AXA_FAIL("impossible axa_tls_start() result");
391 }
392 break;
393
394 case AXA_IO_TYPE_APIKEY:
395 connect_result = socket_connect(emsg, client);
396 if (connect_result != AXA_CONNECT_DONE) {
397 axa_io_pvers_set(&client->io, pvers_save);
398 return (connect_result);
399 }
400 switch (axa_apikey_start(emsg, &client->io)) {
401 case AXA_IO_OK:
402 /* username field holds apikey */
403 if (!axa_client_send(emsg, client,
404 AXA_TAG_NONE, AXA_P_OP_USER, &hdr,
405 &client->io.user,
406 sizeof(client->io.user))) {
407 axa_client_backoff(client);
408 axa_io_pvers_set(&client->io, pvers_save);
409 return (AXA_CONNECT_ERR);
410 }
411 axa_p_to_str(emsg->c, sizeof(emsg->c),
412 true, &hdr,
413 (axa_p_body_t *)&client->io.user);
414 axa_io_pvers_set(&client->io, pvers_save);
415 return (AXA_CONNECT_USER);
416 break;
417 case AXA_IO_ERR:
418 axa_client_backoff_max(client);
419 axa_io_pvers_set(&client->io, pvers_save);
420 return (AXA_CONNECT_ERR);
421 case AXA_IO_BUSY:
422 AXA_ASSERT(client->io.nonblock);
423 axa_io_pvers_set(&client->io, pvers_save);
424 return (connect_result);
425 case AXA_IO_TUNERR:
426 case AXA_IO_KEEPALIVE:
427 AXA_FAIL("impossible axa_apikey_start() result");
428 }
429 break;
430
431 case AXA_IO_TYPE_UNKN:
432 default:
433 axa_pemsg(emsg, "impossible client type");
434 axa_client_backoff_max(client);
435 axa_io_pvers_set(&client->io, pvers_save);
436 return (AXA_CONNECT_ERR);
437 }
438
439 /* Send a NOP if we didn't send the user name. */
440 if (!axa_client_send(emsg, client, AXA_TAG_NONE, AXA_P_OP_NOP,
441 &hdr, NULL, 0)) {
442 axa_client_backoff(client);
443 axa_io_pvers_set(&client->io, pvers_save);
444 return (AXA_CONNECT_ERR);
445 }
446 axa_p_to_str(emsg->c, sizeof(emsg->c), true,
447 &hdr, (axa_p_body_t *)&client->io.user);
448 return (AXA_CONNECT_NOP);
449 }
450
451 axa_connect_result_t
axa_client_open(axa_emsg_t * emsg,axa_client_t * client,const char * addr,bool is_rad,bool tun_debug,int bufsize,bool nonblock)452 axa_client_open(axa_emsg_t *emsg, axa_client_t *client, const char *addr,
453 bool is_rad, bool tun_debug, int bufsize, bool nonblock)
454 {
455 struct addrinfo *ai;
456 const char *p;
457 int i;
458
459 axa_client_close(client);
460
461 client->io.is_rad = is_rad;
462 client->io.is_client = true;
463 client->io.tun_debug = tun_debug;
464 client->io.nonblock = nonblock;
465 client->io.bufsize = bufsize;
466 gettimeofday(&client->retry, NULL);
467
468 p = strpbrk(addr, AXA_WHITESPACE":");
469 if (p == NULL) {
470 axa_pemsg(emsg,
471 "missing AXA transport delimiter in \"%s\"",
472 addr);
473 axa_client_backoff_max(client);
474 return (AXA_CONNECT_ERR);
475 }
476
477 client->io.type = axa_io_type_parse(&addr);
478 if (client->io.type == AXA_IO_TYPE_UNKN) {
479 axa_pemsg(emsg,
480 "invalid AXA transport protocol or alias in \"%s\"",
481 addr);
482 axa_client_backoff_max(client);
483 return (AXA_CONNECT_ERR);
484 }
485
486 if (addr[0] == '-' || addr[0] == '\0') {
487 axa_pemsg(emsg, "invalid server \"%s\"", addr);
488 axa_client_backoff_max(client);
489 return (AXA_CONNECT_ERR);
490 }
491
492 p = strchr(addr, '@');
493 if (p == NULL) {
494 i = 0;
495 } else {
496 i = p - addr;
497 /* Collect the user name from protocols that provide it. */
498 if (client->io.type != AXA_IO_TYPE_TLS) {
499 if (i >= (int)sizeof(client->io.user.name)) {
500 axa_pemsg(emsg,
501 "server user name \"%.*s\" too long",
502 i, addr);
503 axa_client_backoff_max(client);
504 return (AXA_CONNECT_ERR);
505 }
506 memcpy(client->io.user.name, addr, i);
507 }
508 ++i;
509 }
510 if (addr[0] == '-' || addr[0] == '\0'
511 || addr[i] == '-' || addr[i] == '\0') {
512 axa_pemsg(emsg, "invalid server name \"%s\"", addr);
513 axa_client_backoff_max(client);
514 return (AXA_CONNECT_ERR);
515 }
516
517 switch (client->io.type) {
518 case AXA_IO_TYPE_UNIX:
519 client->io.addr = axa_strdup(addr+i);
520 client->io.label = axa_strdup(client->io.addr);
521 client->io.su.sa.sa_family = AF_UNIX;
522 strlcpy(client->io.su.sun.sun_path, client->io.addr,
523 sizeof(client->io.su.sun.sun_path));
524 #ifdef HAVE_SA_LEN
525 client->io.su.sun.sun_len = SUN_LEN(&client->io.su.sun);
526 #endif
527 break;
528
529 case AXA_IO_TYPE_TCP:
530 client->io.addr = axa_strdup(addr+i);
531 client->io.label = axa_strdup(client->io.addr);
532 if (!axa_get_srvr(emsg, client->io.addr, false, &ai)) {
533 axa_client_backoff(client);
534 return (AXA_CONNECT_ERR);
535 }
536 memcpy(&client->io.su.sa, ai->ai_addr, ai->ai_addrlen);
537 freeaddrinfo(ai);
538 break;
539
540 case AXA_IO_TYPE_SSH:
541 client->io.addr = axa_strdup(addr);
542 client->io.label = axa_strdup(addr);
543 break;
544
545 case AXA_IO_TYPE_TLS:
546 if (!axa_tls_parse(emsg, &client->io.cert_file,
547 &client->io.key_file,
548 &client->io.addr,
549 addr))
550 return (AXA_CONNECT_ERR);
551 client->io.label = axa_strdup(client->io.addr);
552 if (!axa_get_srvr(emsg, client->io.addr, false, &ai)) {
553 axa_client_backoff(client);
554 return (AXA_CONNECT_ERR);
555 }
556 memcpy(&client->io.su.sa, ai->ai_addr, ai->ai_addrlen);
557 freeaddrinfo(ai);
558 break;
559
560 case AXA_IO_TYPE_APIKEY:
561 if (!axa_apikey_parse(emsg, &client->io.addr,
562 &client->io.user, addr))
563 return (AXA_CONNECT_ERR);
564 client->io.label = axa_strdup(client->io.addr);
565 if (!axa_get_srvr(emsg, client->io.addr, false, &ai)) {
566 axa_client_backoff(client);
567 return (AXA_CONNECT_ERR);
568 }
569 memcpy(&client->io.su.sa, ai->ai_addr, ai->ai_addrlen);
570 freeaddrinfo(ai);
571 break;
572
573 case AXA_IO_TYPE_UNKN:
574 default:
575 AXA_FAIL("impossible client type");
576 }
577
578 return (axa_client_connect(emsg, client));
579 }
580
581 bool
axa_client_send(axa_emsg_t * emsg,axa_client_t * client,axa_tag_t tag,axa_p_op_t op,axa_p_hdr_t * hdr,const void * body,size_t body_len)582 axa_client_send(axa_emsg_t *emsg, axa_client_t *client,
583 axa_tag_t tag, axa_p_op_t op, axa_p_hdr_t *hdr,
584 const void *body, size_t body_len)
585 {
586 axa_io_result_t io_result;
587
588 if (!AXA_CLIENT_CONNECTED(client)) {
589 axa_pemsg(emsg, "not connected before output");
590 return (false);
591 }
592 io_result = axa_send(emsg, &client->io, tag, op, hdr,
593 body, body_len, NULL, 0);
594 switch (io_result) {
595 case AXA_IO_OK:
596 break;
597 case AXA_IO_BUSY:
598 strlcpy(emsg->c, "output busy", sizeof(emsg->c));
599 return (false);
600 case AXA_IO_ERR:
601 return (false);
602 case AXA_IO_TUNERR:
603 case AXA_IO_KEEPALIVE:
604 default:
605 AXA_FAIL("impossible axa_send() result");
606 }
607
608 return (true);
609 }
610
611 bool
axa_client_get_hello_string(axa_emsg_t * emsg,const char * origin,axa_client_t * client,char ** out)612 axa_client_get_hello_string(axa_emsg_t *emsg, const char *origin,
613 axa_client_t *client, char **out)
614 {
615 int yajl_rc;
616 yajl_gen g = NULL;
617 struct axa_strbuf *sb = NULL;
618 char hostname[HOST_NAME_MAX] = {0};
619 struct utsname utsbuf;
620
621 sb = axa_strbuf_init();
622 if (sb == NULL) {
623 axa_pemsg(emsg, "could not allocate axa_strbuf");
624 return (false);
625 }
626
627 g = yajl_gen_alloc(NULL);
628 AXA_ASSERT (g != NULL);
629
630 yajl_rc = yajl_gen_config(g,
631 yajl_gen_print_callback,
632 _callback_print_yajl_axa_strbuf,
633 sb);
634 AXA_ASSERT(yajl_rc != 0);
635
636 add_yajl_map(g);
637
638 if (0 > gethostname(hostname, sizeof(hostname) - 1)) {
639 axa_pemsg(emsg, "gethostname(): %s", strerror(errno));
640 goto err;
641 }
642 add_yajl_string(g, "hostname");
643 add_yajl_string(g, hostname);
644
645 if (uname(&utsbuf) < 0) {
646 axa_pemsg(emsg, "uname(): %s", strerror(errno));
647 goto err;
648 } else {
649 add_yajl_string(g, "uname_sysname");
650 add_yajl_string(g, utsbuf.sysname);
651 add_yajl_string(g, "uname_release");
652 add_yajl_string(g, utsbuf.release);
653 add_yajl_string(g, "uname_version");
654 add_yajl_string(g, utsbuf.version);
655 add_yajl_string(g, "uname_machine");
656 add_yajl_string(g, utsbuf.machine);
657 }
658
659 add_yajl_string(g, "origin");
660 if (origin == NULL) {
661 add_yajl_string(g, "unknown");
662 }
663 else {
664 add_yajl_string(g, origin);
665 }
666
667 add_yajl_string(g, "libaxa");
668 add_yajl_string(g, axa_get_version());
669
670 add_yajl_string(g, "libnmsg");
671 #ifdef NMSG_LIBRARY_VERSION
672 add_yajl_string(g, nmsg_get_version());
673 #else
674 add_yajl_string(g, "<=0.13.2");
675 #endif
676 add_yajl_string(g, "libwdns");
677 #ifdef WDNS_LIBRARY_VERSION
678 add_yajl_string(g, wdns_get_version());
679 #else
680 add_yajl_string(g, "<=0.9.1");
681 #endif
682 add_yajl_string(g, "libyajl");
683 add_yajl_integer(g, yajl_version());
684 add_yajl_string(g, "OpenSSL");
685 add_yajl_string(g, SSLeay_version(SSLEAY_VERSION));
686 add_yajl_string(g, "libprotobuf-c");
687 add_yajl_string(g, PROTOBUF_C_VERSION);
688
689 add_yajl_string(g, "AXA protocol");
690 add_yajl_integer(g, client->io.pvers);
691
692 close_yajl_map(g);
693
694 yajl_gen_reset(g, "");
695 yajl_gen_free(g);
696
697 *out = sb->data;
698 /* Don't call axa_strbuf_destroy() here, rather, free the wrapper
699 * structure now and leave it to the caller to release sb->data. */
700 free(sb);
701
702 return (true);
703 err:
704 if (g != NULL)
705 yajl_gen_free(g);
706 axa_strbuf_destroy(&sb);
707 return (false);
708 }
709
710 /* Process AXA_P_OP_HELLO from the server. */
711 bool
axa_client_hello(axa_emsg_t * emsg,axa_client_t * client,const axa_p_hello_t * hello,const char * origin)712 axa_client_hello(axa_emsg_t *emsg, axa_client_t *client,
713 const axa_p_hello_t *hello, const char *origin)
714 {
715 axa_p_hdr_t hdr;
716 axa_p_hello_t *cl_hello;
717 size_t len;
718 char op_buf[AXA_P_OP_STRLEN], *p, *out = NULL;
719
720 /* Assume by default that the incoming HELLO is the latest message
721 * in the client structure. */
722 if (hello == NULL) {
723 if (client->io.recv_body == NULL) {
724 axa_pemsg(emsg, "no received AXA message ready");
725 return (false);
726 }
727 hello = &client->io.recv_body->hello;
728 }
729
730 /* There must be one HELLO per session. */
731 if (client->hello != NULL) {
732 axa_pemsg(emsg, "duplicate %s",
733 axa_op_to_str(op_buf, sizeof(op_buf),
734 AXA_P_OP_HELLO));
735 return (false);
736 }
737 client->hello = axa_strdup(hello->str);
738
739 /* Save bundle ID for AXA_P_OP_JOIN */
740 client->clnt_id = hello->id;
741 client->have_id = true;
742
743 /* Save the protocol version that the server requires. */
744 client->io.pvers = AXA_P_PVERS;
745 if (client->io.pvers < hello->pvers_min)
746 client->io.pvers = hello->pvers_min;
747 if (client->io.pvers > hello->pvers_max)
748 client->io.pvers = hello->pvers_max;
749
750 /* Limit the version to one that we can understand.
751 * Just hope for the best if the server did not offer a version
752 * that we can use. */
753 if (client->io.pvers < AXA_P_PVERS_MIN)
754 client->io.pvers = AXA_P_PVERS_MIN;
755 if (client->io.pvers > AXA_P_PVERS_MAX)
756 client->io.pvers = AXA_P_PVERS_MAX;
757
758 /* client -> server HELLO was introduced in AXA_P_VERS2 */
759 if (hello->pvers_max < AXA_P_PVERS2)
760 return (true);
761
762 cl_hello = AXA_SALLOC(axa_p_hello_t);
763 cl_hello->id = hello->id;
764 cl_hello->pvers_min = AXA_P_PVERS_MIN;
765 cl_hello->pvers_max = AXA_P_PVERS_MAX;
766
767 p = cl_hello->str;
768 len = sizeof (cl_hello->str);
769 if (!axa_client_get_hello_string(emsg, origin, client, &out)) {
770 axa_error_msg("error getting detailed HELLO info: %s",
771 emsg->c);
772 if (origin == NULL)
773 origin = "[unknown]";
774
775 snprintf(cl_hello->str, sizeof (cl_hello->str) - 1,
776 "%s %s AXA protocol %d",
777 origin, axa_get_version(), AXA_P_PVERS);
778 }
779 else {
780 /* Note, if strlen(out) is > sizeof (*p), the json blob will be
781 * fouled. */
782 axa_buf_print(&p, &len, "%s", out);
783 free(out);
784 }
785
786 axa_client_send(emsg, client, AXA_TAG_NONE, AXA_P_OP_HELLO, &hdr,
787 cl_hello, sizeof(*cl_hello) -
788 sizeof(cl_hello->str) + strlen(cl_hello->str) + 1);
789 free(cl_hello);
790
791 return (true);
792 }
793