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