1 /*
2 * Copyright (C) 2018-2019 Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the Free
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 #include <stdlib.h>
24 #include <string.h>
25
26 #ifdef HAVE_INTTYPES_H
27 # include <inttypes.h>
28 #endif
29
30 #ifdef HAVE_SYS_SELECT_H
31 # include <sys/select.h>
32 #else
33 # include <sys/time.h>
34 # include <sys/types.h>
35 # include <unistd.h>
36 #endif
37
38 #include <shout/shout.h>
39 #include "shout_private.h"
40
41 #ifdef HAVE_OPENSSL
shout_cb_tls_callback(shout_tls_t * tls,shout_event_t event,void * userdata,va_list ap)42 static int shout_cb_tls_callback(shout_tls_t *tls, shout_event_t event, void *userdata, va_list ap)
43 {
44 shout_connection_t *con = userdata;
45
46 if (!con->callback)
47 return SHOUT_CALLBACK_PASS;
48
49 return con->callback(con, event, con->callback_userdata, ap);
50 }
51 #endif
52
shout_connection_new(shout_t * self,const shout_protocol_impl_t * impl,const void * plan)53 shout_connection_t *shout_connection_new(shout_t *self, const shout_protocol_impl_t *impl, const void *plan)
54 {
55 shout_connection_t *con;
56
57 if (!self || !impl)
58 return NULL;
59
60 con = calloc(1, sizeof(*con));
61 if (!con)
62 return NULL;
63
64 con->refc = 1;
65 con->socket = SOCK_ERROR;
66 con->selected_tls_mode = SHOUT_TLS_AUTO;
67 con->impl = impl;
68 con->plan = plan;
69 con->error = SHOUTERR_SUCCESS;
70
71 return con;
72 }
73
shout_connection_ref(shout_connection_t * con)74 int shout_connection_ref(shout_connection_t *con)
75 {
76 if (!con)
77 return SHOUTERR_INSANE;
78
79 con->refc++;
80
81 return SHOUTERR_SUCCESS;
82 }
83
shout_connection_unref(shout_connection_t * con)84 int shout_connection_unref(shout_connection_t *con)
85 {
86 if (!con)
87 return SHOUTERR_INSANE;
88
89 con->refc--;
90
91 if (con->refc)
92 return SHOUTERR_SUCCESS;
93
94 if (con->destory)
95 con->destory(con);
96
97 shout_connection_disconnect(con);
98
99 free(con);
100
101 return SHOUTERR_SUCCESS;
102 }
103
shout_connection_iter__wait_for_io__get_timeout(shout_connection_t * con,shout_t * shout,uint64_t timeout)104 static struct timeval shout_connection_iter__wait_for_io__get_timeout(shout_connection_t *con, shout_t *shout, uint64_t timeout)
105 {
106 static const struct timeval tv_blocking = {
107 .tv_sec = 8,
108 .tv_usec = 0
109 };
110 static const struct timeval tv_nonblocking = {
111 .tv_sec = 0,
112 .tv_usec = 1000
113 };
114
115 if (timeout) {
116 struct timeval tv = {
117 .tv_sec = timeout / 1000,
118 .tv_usec = (timeout % 1000) * 1000
119 };
120 return tv;
121 } else if (con->nonblocking == SHOUT_BLOCKING_NONE) {
122 return tv_nonblocking;
123 } else {
124 return tv_blocking;
125 }
126 }
127
shout_connection_iter__wait_for_io(shout_connection_t * con,shout_t * shout,int for_read,int for_write,uint64_t timeout)128 static shout_connection_return_state_t shout_connection_iter__wait_for_io(shout_connection_t *con, shout_t *shout, int for_read, int for_write, uint64_t timeout)
129 {
130 struct timeval tv = shout_connection_iter__wait_for_io__get_timeout(con, shout, timeout);
131 fd_set fhset_r;
132 fd_set fhset_w;
133 fd_set fhset_e;
134 int ret;
135
136 FD_ZERO(&fhset_r);
137 FD_ZERO(&fhset_w);
138 FD_ZERO(&fhset_e);
139 FD_SET(con->socket, &fhset_r);
140 FD_SET(con->socket, &fhset_w);
141 FD_SET(con->socket, &fhset_e);
142
143 ret = select(con->socket + 1, (for_read) ? &fhset_r : NULL, (for_write) ? &fhset_w : NULL, &fhset_e, &tv);
144
145 if (ret > 0 && (FD_ISSET(con->socket, &fhset_r) || FD_ISSET(con->socket, &fhset_w) || FD_ISSET(con->socket, &fhset_e))) {
146 return SHOUT_RS_DONE;
147 } else if (ret == 0) {
148 shout_connection_set_error(con, SHOUTERR_RETRY);
149 return SHOUT_RS_TIMEOUT;
150 } else {
151 shout_connection_set_error(con, SHOUTERR_SOCKET);
152 return SHOUT_RS_ERROR;
153 }
154 }
155
shout_connection_iter__socket(shout_connection_t * con,shout_t * shout)156 static shout_connection_return_state_t shout_connection_iter__socket(shout_connection_t *con, shout_t *shout)
157 {
158 shout_connection_return_state_t ret;
159 int rc;
160
161 switch (con->current_socket_state) {
162 case SHOUT_SOCKSTATE_UNCONNECTED:
163 shout_connection_set_error(con, shout_connection_connect(con, shout));
164 if (shout_connection_get_error(con) == SHOUTERR_SUCCESS) {
165 con->current_socket_state = SHOUT_SOCKSTATE_CONNECTING;
166 return SHOUT_RS_DONE;
167 }
168 break;
169 case SHOUT_SOCKSTATE_CONNECTING:
170 if (con->nonblocking == SHOUT_BLOCKING_NONE) {
171 ret = shout_connection_iter__wait_for_io(con, shout, 1, 1, 0);
172 if (ret != SHOUT_RS_DONE) {
173 return ret;
174 }
175 }
176
177 if (sock_connected(con->socket, 0) == 1) {
178 con->current_socket_state = SHOUT_SOCKSTATE_CONNECTED;
179 return SHOUT_RS_DONE;
180 }
181 break;
182 #ifdef HAVE_OPENSSL
183 case SHOUT_SOCKSTATE_CONNECTED:
184 shout_tls_try_connect(con->tls);
185 con->current_socket_state = SHOUT_SOCKSTATE_TLS_CONNECTING;
186 return SHOUT_RS_DONE;
187 break;
188 case SHOUT_SOCKSTATE_TLS_CONNECTING:
189 case SHOUT_SOCKSTATE_TLS_CONNECTED:
190 rc = shout_tls_try_connect(con->tls);
191 if (rc == SHOUTERR_SUCCESS) {
192 con->current_socket_state = SHOUT_SOCKSTATE_TLS_VERIFIED;
193 return SHOUT_RS_DONE;
194 } else if (rc == SHOUTERR_BUSY) {
195 return SHOUT_RS_NOTNOW;
196 } else {
197 shout_connection_set_error(con, rc);
198 return SHOUT_RS_ERROR;
199 }
200 break;
201 #else
202 case SHOUT_SOCKSTATE_CONNECTED:
203 case SHOUT_SOCKSTATE_TLS_CONNECTING:
204 case SHOUT_SOCKSTATE_TLS_CONNECTED:
205 shout_connection_set_error(con, SHOUTERR_UNSUPPORTED);
206 return SHOUT_RS_ERROR;
207 break;
208 #endif
209 }
210
211 shout_connection_set_error(con, SHOUTERR_SOCKET);
212 return SHOUT_RS_ERROR;
213 }
214
shout_connection__read(shout_connection_t * con,shout_t * shout,void * buf,size_t len)215 ssize_t shout_connection__read(shout_connection_t *con, shout_t *shout, void *buf, size_t len)
216 {
217 #ifdef HAVE_OPENSSL
218 if (con->tls)
219 return shout_tls_read(con->tls, buf, len);
220 #endif
221 return sock_read_bytes(con->socket, buf, len);
222 }
223
shout_connection__write(shout_connection_t * con,shout_t * shout,const void * buf,size_t len)224 ssize_t shout_connection__write(shout_connection_t *con, shout_t *shout, const void *buf, size_t len)
225 {
226 #ifdef HAVE_OPENSSL
227 if (con->tls)
228 return shout_tls_write(con->tls, buf, len);
229 #endif
230 return sock_write_bytes(con->socket, buf, len);
231 }
shout_connection__recoverable(shout_connection_t * con,shout_t * shout)232 int shout_connection__recoverable(shout_connection_t *con, shout_t *shout)
233 {
234 #ifdef HAVE_OPENSSL
235 if (con->tls)
236 return shout_tls_recoverable(con->tls);
237 #endif
238 return sock_recoverable(sock_error());
239 }
240
try_write(shout_connection_t * con,shout_t * shout,const void * data_p,size_t len)241 static ssize_t try_write(shout_connection_t *con, shout_t *shout, const void *data_p, size_t len)
242 {
243 ssize_t ret;
244 size_t pos = 0;
245 unsigned char *data = (unsigned char*)data_p;
246
247 /* loop until whole buffer is written (unless it would block) */
248 do {
249 ret = shout_connection__write(con, shout, data + pos, len - pos);
250 if (ret > 0)
251 pos += ret;
252 } while (pos < len && ret >= 0);
253
254 if (ret < 0) {
255 if (shout_connection__recoverable(con, shout)) {
256 shout_connection_set_error(con, SHOUTERR_BUSY);
257 return pos;
258 }
259 shout_connection_set_error(con, SHOUTERR_SOCKET);
260 return ret;
261 }
262 return pos;
263 }
264
shout_connection_iter__message__send_queue(shout_connection_t * con,shout_t * shout)265 static shout_connection_return_state_t shout_connection_iter__message__send_queue(shout_connection_t *con, shout_t *shout)
266 {
267 shout_buf_t *buf;
268 int ret;
269
270 if (!con->wqueue.len)
271 return SHOUT_RS_DONE;
272
273 buf = con->wqueue.head;
274 while (buf) {
275 ret = try_write(con, shout, buf->data + buf->pos, buf->len - buf->pos);
276 if (ret < 0) {
277 if (shout_connection_get_error(con) == SHOUTERR_BUSY) {
278 return SHOUT_RS_NOTNOW;
279 } else {
280 return SHOUT_RS_ERROR;
281 }
282 }
283
284 buf->pos += ret;
285 con->wqueue.len -= ret;
286 if (buf->pos == buf->len) {
287 con->wqueue.head = buf->next;
288 free(buf);
289 buf = con->wqueue.head;
290 if (buf)
291 buf->prev = NULL;
292 } else {
293 /* incomplete write */
294 return SHOUT_RS_NOTNOW;
295 }
296 }
297 return SHOUT_RS_DONE;
298 }
299
shout_connection_iter__message__recv(shout_connection_t * con,shout_t * shout)300 static shout_connection_return_state_t shout_connection_iter__message__recv(shout_connection_t *con, shout_t *shout)
301 {
302 char buf[1024];
303 ssize_t rc;
304 int ret;
305
306 rc = shout_connection__read(con, shout, buf, sizeof(buf));
307
308 if (rc < 0 && shout_connection__recoverable(con, shout))
309 return SHOUT_RS_NOTNOW;
310
311 if (rc > 0) {
312 if ((ret = shout_queue_data(&(con->rqueue), (unsigned char*)buf, rc)) != SHOUTERR_SUCCESS) {
313 shout_connection_set_error(con, ret);
314 return SHOUT_RS_ERROR;
315 }
316 }
317
318 return con->impl->msg_get(shout, con);
319 }
shout_connection_iter__message(shout_connection_t * con,shout_t * shout)320 static shout_connection_return_state_t shout_connection_iter__message(shout_connection_t *con, shout_t *shout)
321 {
322 shout_connection_return_state_t ret = SHOUT_RS_DONE;
323
324 switch (con->current_message_state) {
325 case SHOUT_MSGSTATE_IDLE:
326 return SHOUT_RS_DONE;
327 break;
328 case SHOUT_MSGSTATE_CREATING0:
329 case SHOUT_MSGSTATE_CREATING1:
330 if (con->impl->msg_create) {
331 ret = con->impl->msg_create(shout, con);
332 }
333 if (ret == SHOUT_RS_DONE) {
334 if (con->current_message_state == SHOUT_MSGSTATE_CREATING0) {
335 con->current_message_state = SHOUT_MSGSTATE_SENDING0;
336 } else {
337 con->current_message_state = SHOUT_MSGSTATE_SENDING1;
338 }
339 }
340 return ret;
341 break;
342 case SHOUT_MSGSTATE_SENDING0:
343 ret = shout_connection_iter__message__send_queue(con, shout);
344 if (ret == SHOUT_RS_DONE) {
345 con->current_message_state = SHOUT_MSGSTATE_WAITING0;
346 }
347 return ret;
348 break;
349 case SHOUT_MSGSTATE_SENDING1:
350 if (con->wqueue.len) {
351 return shout_connection_iter__message__send_queue(con, shout);
352 } else {
353 shout_connection_set_error(con, SHOUTERR_SUCCESS);
354 return SHOUT_RS_ERROR;
355 }
356 break;
357 case SHOUT_MSGSTATE_WAITING0:
358 case SHOUT_MSGSTATE_WAITING1:
359 if (con->wait_timeout) {
360 uint64_t now = timing_get_time();
361 if (now > con->wait_timeout) {
362 if (con->current_message_state == SHOUT_MSGSTATE_WAITING0) {
363 con->current_message_state = SHOUT_MSGSTATE_RECEIVED0;
364 } else {
365 con->current_message_state = SHOUT_MSGSTATE_RECEIVING1;
366 }
367 con->wait_timeout_happened = 1;
368 return SHOUT_RS_DONE;
369 } else {
370 ret = shout_connection_iter__wait_for_io(con, shout, 1, 0, con->wait_timeout - now);
371 }
372 } else {
373 ret = shout_connection_iter__wait_for_io(con, shout, 1, 0, 0);
374 }
375 if (ret == SHOUT_RS_DONE) {
376 if (con->current_message_state == SHOUT_MSGSTATE_WAITING0) {
377 con->current_message_state = SHOUT_MSGSTATE_RECEIVING0;
378 } else {
379 con->current_message_state = SHOUT_MSGSTATE_RECEIVING1;
380 }
381 }
382 return ret;
383 break;
384 case SHOUT_MSGSTATE_RECEIVING0:
385 case SHOUT_MSGSTATE_RECEIVING1:
386 ret = shout_connection_iter__message__recv(con, shout);
387 if (ret == SHOUT_RS_DONE) {
388 if (con->current_message_state == SHOUT_MSGSTATE_RECEIVING0) {
389 con->current_message_state = SHOUT_MSGSTATE_RECEIVED0;
390 } else {
391 con->current_message_state = SHOUT_MSGSTATE_RECEIVING1;
392 }
393 }
394 return ret;
395 break;
396 case SHOUT_MSGSTATE_RECEIVED0:
397 case SHOUT_MSGSTATE_RECEIVED1:
398 if (con->impl->msg_parse)
399 ret = con->impl->msg_parse(shout, con);
400 shout_connection_set_wait_timeout(con, shout, 0);
401 return ret;
402 break;
403 case SHOUT_MSGSTATE_PARSED_INFORMATIONAL0:
404 case SHOUT_MSGSTATE_PARSED_INFORMATIONAL1:
405 con->current_message_state = SHOUT_MSGSTATE_CREATING1;
406 return SHOUT_RS_DONE;
407 break;
408 case SHOUT_MSGSTATE_PARSED_FINAL:
409 con->current_message_state = SHOUT_MSGSTATE_IDLE;
410 return SHOUT_RS_DONE;
411 break;
412 }
413
414 shout_connection_set_error(con, SHOUTERR_SOCKET);
415 return SHOUT_RS_ERROR;
416 }
417
shout_connection_iter__protocol(shout_connection_t * con,shout_t * shout)418 static shout_connection_return_state_t shout_connection_iter__protocol(shout_connection_t *con, shout_t *shout)
419 {
420 shout_connection_return_state_t ret;
421
422 if (!con->impl->protocol_iter) {
423 con->current_protocol_state = con->target_protocol_state;
424 return SHOUT_RS_DONE;
425 }
426
427 ret = con->impl->protocol_iter(shout, con);
428 switch (ret) {
429 case SHOUT_RS_ERROR:
430 shout_connection_set_error(con, SHOUTERR_SOCKET);
431 break;
432 case SHOUT_RS_NOTNOW:
433 case SHOUT_RS_TIMEOUT:
434 shout_connection_set_error(con, SHOUTERR_RETRY);
435 break;
436 }
437
438 return ret;
439 }
440
shout_connection_iter(shout_connection_t * con,shout_t * shout)441 int shout_connection_iter(shout_connection_t *con, shout_t *shout)
442 {
443 int found;
444 int retry;
445
446 if (!con || !shout)
447 return SHOUTERR_INSANE;
448
449 if (con->socket == SOCK_ERROR)
450 return SHOUTERR_NOCONNECT;
451
452
453 #define __iter(what) \
454 while (!retry && con->target_ ## what ## _state != con->current_ ## what ## _state) { \
455 found = 1; \
456 shout_connection_return_state_t ret = shout_connection_iter__ ## what (con, shout); \
457 switch (ret) { \
458 case SHOUT_RS_DONE: \
459 continue; \
460 break; \
461 case SHOUT_RS_TIMEOUT: \
462 case SHOUT_RS_NOTNOW: \
463 if (con->nonblocking == SHOUT_BLOCKING_NONE) \
464 return SHOUTERR_RETRY; \
465 retry = 1; \
466 break; \
467 case SHOUT_RS_ERROR: \
468 return shout_connection_get_error(con); \
469 break; \
470 } \
471 }
472
473 do {
474 found = 0;
475 retry = 0;
476 __iter(socket)
477 __iter(message)
478 __iter(protocol)
479 } while (found || retry);
480
481 return SHOUTERR_SUCCESS;
482 }
483
shout_connection_select_tlsmode(shout_connection_t * con,int tlsmode)484 int shout_connection_select_tlsmode(shout_connection_t *con, int tlsmode)
485 {
486 if (!con)
487 return SHOUTERR_INSANE;
488
489 if (tlsmode == con->selected_tls_mode)
490 return SHOUTERR_SUCCESS;
491
492 #ifdef HAVE_OPENSSL
493 if (con->tls)
494 return SHOUTERR_BUSY;
495 #endif
496
497 if (con->selected_tls_mode != SHOUT_TLS_AUTO && con->selected_tls_mode != SHOUT_TLS_AUTO_NO_PLAIN)
498 return SHOUTERR_BUSY;
499
500 if ((tlsmode == SHOUT_TLS_DISABLED || tlsmode == SHOUT_TLS_AUTO) && con->selected_tls_mode == SHOUT_TLS_AUTO_NO_PLAIN)
501 return SHOUTERR_NOTLS;
502
503 switch (tlsmode) {
504 case SHOUT_TLS_DISABLED:
505 case SHOUT_TLS_AUTO:
506 case SHOUT_TLS_AUTO_NO_PLAIN:
507 case SHOUT_TLS_RFC2818:
508 case SHOUT_TLS_RFC2817:
509 con->selected_tls_mode = tlsmode;
510 return SHOUTERR_SUCCESS;
511 break;
512 default:
513 return SHOUTERR_INSANE;
514 break;
515 }
516
517 return SHOUTERR_INSANE;
518 }
shout_connection_set_nonblocking(shout_connection_t * con,unsigned int nonblocking)519 int shout_connection_set_nonblocking(shout_connection_t *con, unsigned int nonblocking)
520 {
521 if (!con || (nonblocking != SHOUT_BLOCKING_DEFAULT && nonblocking != SHOUT_BLOCKING_FULL && nonblocking != SHOUT_BLOCKING_NONE))
522 return SHOUTERR_INSANE;
523
524 if (con->socket != SOCK_ERROR)
525 return SHOUTERR_BUSY;
526
527 con->nonblocking = nonblocking;
528
529 return SHOUTERR_SUCCESS;
530 }
531
shout_connection_set_wait_timeout(shout_connection_t * con,shout_t * shout,uint64_t timeout)532 int shout_connection_set_wait_timeout(shout_connection_t *con, shout_t *shout, uint64_t timeout /* [ms] */)
533 {
534 if (!con || !shout)
535 return SHOUTERR_INSANE;
536
537 if (timeout) {
538 con->wait_timeout = timing_get_time() + timeout;
539 } else {
540 con->wait_timeout = 0;
541 }
542
543 con->wait_timeout_happened = 0;
544
545 return SHOUTERR_SUCCESS;
546 }
547
shout_connection_get_wait_timeout_happened(shout_connection_t * con,shout_t * shout)548 int shout_connection_get_wait_timeout_happened(shout_connection_t *con, shout_t *shout) /* returns SHOUTERR_* or > 0 for true */
549 {
550 if (!con || !shout)
551 return SHOUTERR_INSANE;
552
553 return con->wait_timeout_happened;
554 }
555
shout_connection_connect(shout_connection_t * con,shout_t * shout)556 int shout_connection_connect(shout_connection_t *con, shout_t *shout)
557 {
558 int port;
559
560 if (!con || !shout)
561 return SHOUTERR_INSANE;
562
563 if (con->socket != SOCK_ERROR || con->current_socket_state != SHOUT_SOCKSTATE_UNCONNECTED)
564 return SHOUTERR_BUSY;
565
566 if (con->nonblocking != SHOUT_BLOCKING_DEFAULT)
567 shout_connection_set_nonblocking(con, shout_get_nonblocking(shout));
568
569 port = shout->port;
570 if (con->impl == shout_icy_impl)
571 port++;
572
573 if (con->nonblocking == SHOUT_BLOCKING_NONE) {
574 con->socket = sock_connect_non_blocking(shout->host, port);
575 } else {
576 con->socket = sock_connect(shout->host, port);
577 }
578
579 if (con->socket < 0) {
580 con->socket = SOCK_ERROR;
581 return SHOUTERR_NOCONNECT;
582 }
583
584 con->current_socket_state = SHOUT_SOCKSTATE_CONNECTING;
585 con->target_socket_state = SHOUT_SOCKSTATE_CONNECTED;
586 if (con->target_message_state != SHOUT_MSGSTATE_IDLE)
587 con->current_message_state = SHOUT_MSGSTATE_CREATING0;
588
589 if (con->selected_tls_mode == SHOUT_TLS_RFC2818)
590 return shout_connection_starttls(con, shout);
591
592 return SHOUTERR_SUCCESS;
593 }
shout_connection_disconnect(shout_connection_t * con)594 int shout_connection_disconnect(shout_connection_t *con)
595 {
596 if (!con)
597 return SHOUTERR_INSANE;
598
599 #ifdef HAVE_OPENSSL
600 if (con->tls)
601 shout_tls_close(con->tls);
602 con->tls = NULL;
603 #endif
604
605 if (con->socket != SOCK_ERROR)
606 sock_close(con->socket);
607 con->socket = SOCK_ERROR;
608
609 con->target_socket_state = SHOUT_SOCKSTATE_UNCONNECTED;
610 con->current_socket_state = SHOUT_SOCKSTATE_UNCONNECTED;
611
612 return SHOUTERR_SUCCESS;
613 }
shout_connection_send(shout_connection_t * con,shout_t * shout,const void * buf,size_t len)614 ssize_t shout_connection_send(shout_connection_t *con, shout_t *shout, const void *buf, size_t len)
615 {
616 int ret;
617
618 if (!con || !shout)
619 return -1;
620
621 if (con->current_message_state != SHOUT_MSGSTATE_SENDING1)
622 return -1;
623
624 if (con->error == SHOUTERR_SOCKET)
625 return -1;
626
627 ret = shout_queue_data(&(con->wqueue), buf, len);
628 if (ret != SHOUTERR_SUCCESS) {
629 shout_connection_set_error(con, ret);
630 return -1;
631 }
632
633 shout_connection_iter(con, shout);
634
635 return len;
636 }
637
shout_connection_get_sendq(shout_connection_t * con,shout_t * shout)638 ssize_t shout_connection_get_sendq(shout_connection_t *con, shout_t *shout)
639 {
640 if (!con || !shout)
641 return -1;
642
643 return con->wqueue.len;
644 }
645
shout_connection_starttls(shout_connection_t * con,shout_t * shout)646 int shout_connection_starttls(shout_connection_t *con, shout_t *shout)
647 {
648 #ifdef HAVE_OPENSSL
649 if (!con || !shout)
650 return SHOUTERR_INSANE;
651
652 if (con->tls)
653 return SHOUTERR_BUSY;
654
655 con->tls = shout_tls_new(shout, con->socket);
656 if (!con->tls) /* just guessing that it's a malloc error */
657 return SHOUTERR_MALLOC;
658
659 shout_tls_set_callback(con->tls, shout_cb_tls_callback, con);
660
661 con->target_socket_state = SHOUT_SOCKSTATE_TLS_VERIFIED;
662
663 return SHOUTERR_SUCCESS;
664 #else
665 return SHOUTERR_UNSUPPORTED;
666 #endif
667 }
668
shout_connection_set_error(shout_connection_t * con,int error)669 int shout_connection_set_error(shout_connection_t *con, int error)
670 {
671 if (!con)
672 return SHOUTERR_INSANE;
673
674 con->error = error;
675
676 return SHOUTERR_SUCCESS;
677 }
shout_connection_get_error(shout_connection_t * con)678 int shout_connection_get_error(shout_connection_t *con)
679 {
680 if (!con)
681 return SHOUTERR_INSANE;
682
683 return con->error;
684 }
shout_connection_transfer_error(shout_connection_t * con,shout_t * shout)685 int shout_connection_transfer_error(shout_connection_t *con, shout_t *shout)
686 {
687 if (!con || !shout)
688 return SHOUTERR_INSANE;
689
690 shout->error = con->error;
691
692 return SHOUTERR_SUCCESS;
693 }
shout_connection_control(shout_connection_t * con,shout_control_t control,...)694 int shout_connection_control(shout_connection_t *con, shout_control_t control, ...)
695 {
696 int ret = SHOUTERR_INSANE;
697 va_list ap;
698
699 if (!con)
700 return SHOUTERR_INSANE;
701
702 va_start(ap, control);
703
704 switch (control) {
705 #ifdef HAVE_OPENSSL
706 case SHOUT_CONTROL_GET_SERVER_CERTIFICATE_AS_PEM:
707 case SHOUT_CONTROL_GET_SERVER_CERTIFICATE_CHAIN_AS_PEM:
708 if (con->tls) {
709 void **vpp = va_arg(ap, void **);
710 char *buf;
711
712 if (vpp) {
713 if (control == SHOUT_CONTROL_GET_SERVER_CERTIFICATE_AS_PEM) {
714 ret = shout_tls_get_peer_certificate(con->tls, &buf);
715 } else {
716 ret = shout_tls_get_peer_certificate_chain(con->tls, &buf);
717 }
718 if (ret == SHOUTERR_SUCCESS) {
719 *vpp = buf;
720 }
721 } else {
722 ret = SHOUTERR_INSANE;
723 }
724 } else {
725 ret = SHOUTERR_BUSY;
726 }
727 break;
728 #else
729 case SHOUT_CONTROL_GET_SERVER_CERTIFICATE_AS_PEM:
730 case SHOUT_CONTROL_GET_SERVER_CERTIFICATE_CHAIN_AS_PEM:
731 ret = SHOUTERR_UNSUPPORTED;
732 break;
733 #endif
734 case SHOUT_CONTROL__MIN:
735 case SHOUT_CONTROL__MAX:
736 ret = SHOUTERR_INSANE;
737 break;
738 }
739
740 va_end(ap);
741
742 return ret;
743 }
744
shout_connection_set_callback(shout_connection_t * con,shout_connection_callback_t callback,void * userdata)745 int shout_connection_set_callback(shout_connection_t *con, shout_connection_callback_t callback, void *userdata)
746 {
747 if (!con)
748 return SHOUTERR_INSANE;
749
750 con->callback = callback;
751 con->callback_userdata = userdata;
752
753 return SHOUTERR_SUCCESS;
754 }
755