1 /*
2  *  tvheadend, UPnP interface
3  *  Copyright (C) 2014 Jaroslav Kysela
4  *
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <pthread.h>
20 #include <assert.h>
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdarg.h>
26 #include <fcntl.h>
27 #include <errno.h>
28 #include <signal.h>
29 #include <netinet/in.h>
30 #include <netinet/tcp.h>
31 #include <arpa/inet.h>
32 
33 #include "tvheadend.h"
34 #include "tvhpoll.h"
35 #include "upnp.h"
36 
37 #if defined(PLATFORM_FREEBSD) || ENABLE_ANDROID
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #endif
41 
42 int              upnp_running;
43 static pthread_t upnp_tid;
44 pthread_mutex_t  upnp_lock;
45 
46 TAILQ_HEAD(upnp_active_services, upnp_service);
47 
48 typedef struct upnp_data {
49   TAILQ_ENTRY(upnp_data) data_link;
50   struct sockaddr_storage storage;
51   htsbuf_queue_t queue;
52   int delay_ms;
53   int from_multicast;
54 } upnp_data_t;
55 
56 TAILQ_HEAD(upnp_data_queue_write, upnp_data);
57 
58 static struct upnp_active_services upnp_services;
59 static struct upnp_data_queue_write upnp_data_write;
60 static struct sockaddr_storage upnp_ipv4_multicast;
61 
62 /*
63  *
64  */
upnp_service_create0(upnp_service_t * us)65 upnp_service_t *upnp_service_create0( upnp_service_t *us )
66 {
67   pthread_mutex_lock(&upnp_lock);
68   TAILQ_INSERT_TAIL(&upnp_services, us, us_link);
69   pthread_mutex_unlock(&upnp_lock);
70   return us;
71 }
72 
upnp_service_destroy(upnp_service_t * us)73 void upnp_service_destroy( upnp_service_t *us )
74 {
75   pthread_mutex_lock(&upnp_lock);
76   TAILQ_REMOVE(&upnp_services, us, us_link);
77   us->us_destroy(us);
78   pthread_mutex_unlock(&upnp_lock);
79   free(us);
80 }
81 
82 /*
83  *
84  */
85 void
upnp_send(htsbuf_queue_t * q,struct sockaddr_storage * storage,int delay_ms,int from_multicast)86 upnp_send( htsbuf_queue_t *q, struct sockaddr_storage *storage,
87            int delay_ms, int from_multicast )
88 {
89   upnp_data_t *data;
90 
91   if (!atomic_get(&upnp_running))
92     return;
93   data = calloc(1, sizeof(upnp_data_t));
94   htsbuf_queue_init(&data->queue, 0);
95   htsbuf_appendq(&data->queue, q);
96   if (storage == NULL)
97     data->storage = upnp_ipv4_multicast;
98   else
99     data->storage = *storage;
100   data->delay_ms = delay_ms;
101   data->from_multicast = from_multicast;
102   pthread_mutex_lock(&upnp_lock);
103   TAILQ_INSERT_TAIL(&upnp_data_write, data, data_link);
104   pthread_mutex_unlock(&upnp_lock);
105 }
106 
107 /*
108  *
109  */
110 static void
upnp_dump_data(upnp_data_t * data)111 upnp_dump_data( upnp_data_t *data )
112 {
113 #if 0
114   char tbuf[256];
115   inet_ntop(data->storage.ss_family, IP_IN_ADDR(data->storage), tbuf, sizeof(tbuf));
116   printf("upnp out to %s:%d\n", tbuf, ntohs(IP_PORT(data->storage)));
117   htsbuf_hexdump(&data->queue, "upnp out");
118 #endif
119 }
120 
121 /*
122  *  Discovery thread
123  */
124 static void *
upnp_thread(void * aux)125 upnp_thread( void *aux )
126 {
127   char *bindaddr = aux;
128   tvhpoll_t *poll = tvhpoll_create(2);
129   tvhpoll_event_t ev[2];
130   upnp_data_t *data;
131   udp_connection_t *multicast = NULL, *unicast = NULL;
132   udp_connection_t *conn;
133   unsigned char buf[16384];
134   upnp_service_t *us;
135   struct sockaddr_storage ip;
136   socklen_t iplen;
137   size_t size;
138   int r, delay_ms;
139 
140   multicast = udp_bind(LS_UPNP, "upnp_thread_multicast",
141                        "239.255.255.250", 1900, NULL,
142                        NULL, 32*1024, 32*1024);
143   if (multicast == NULL || multicast == UDP_FATAL_ERROR)
144     goto error;
145   unicast = udp_bind(LS_UPNP, "upnp_thread_unicast", bindaddr, 0, NULL,
146                      NULL, 32*1024, 32*1024);
147   if (unicast == NULL || unicast == UDP_FATAL_ERROR)
148     goto error;
149 
150   memset(&ev, 0, sizeof(ev));
151   ev[0].fd       = multicast->fd;
152   ev[0].events   = TVHPOLL_IN;
153   ev[0].data.ptr = multicast;
154   ev[1].fd       = unicast->fd;
155   ev[1].events   = TVHPOLL_IN;
156   ev[1].data.ptr = unicast;
157   tvhpoll_add(poll, ev, 2);
158 
159   delay_ms = 0;
160 
161   while (atomic_get(&upnp_running) && multicast->fd >= 0) {
162     r = tvhpoll_wait(poll, ev, 2, delay_ms ?: 1000);
163     if (r == 0) /* timeout */
164       delay_ms = 0;
165 
166     while (r-- > 0) {
167       if ((ev[r].events & TVHPOLL_IN) != 0) {
168         conn = ev[r].data.ptr;
169         iplen = sizeof(ip);
170         size = recvfrom(conn->fd, buf, sizeof(buf), 0,
171                                            (struct sockaddr *)&ip, &iplen);
172         if (size > 0 && tvhtrace_enabled()) {
173           char tbuf[256];
174           inet_ntop(ip.ss_family, IP_IN_ADDR(ip), tbuf, sizeof(tbuf));
175           tvhtrace(LS_UPNP, "%s - received data from %s:%hu [size=%zi]",
176                    conn == multicast ? "multicast" : "unicast",
177                    tbuf, (unsigned short) ntohs(IP_PORT(ip)), size);
178           tvhlog_hexdump(LS_UPNP, buf, size);
179         }
180         /* TODO: a filter */
181         TAILQ_FOREACH(us, &upnp_services, us_link)
182           us->us_received(buf, size, conn, &ip);
183       }
184     }
185 
186     while (delay_ms == 0) {
187       pthread_mutex_lock(&upnp_lock);
188       data = TAILQ_FIRST(&upnp_data_write);
189       if (data) {
190         delay_ms = data->delay_ms;
191         data->delay_ms = 0;
192         if (!delay_ms) {
193           TAILQ_REMOVE(&upnp_data_write, data, data_link);
194         } else {
195           data = NULL;
196         }
197       }
198       pthread_mutex_unlock(&upnp_lock);
199       if (data == NULL)
200         break;
201       upnp_dump_data(data);
202       udp_write_queue(data->from_multicast ? multicast : unicast,
203                       &data->queue, &data->storage);
204       htsbuf_queue_flush(&data->queue);
205       free(data);
206       delay_ms = 0;
207     }
208   }
209 
210   /* flush the write queue (byebye messages) */
211   while (1) {
212     pthread_mutex_lock(&upnp_lock);
213     data = TAILQ_FIRST(&upnp_data_write);
214     if (data)
215       TAILQ_REMOVE(&upnp_data_write, data, data_link);
216     pthread_mutex_unlock(&upnp_lock);
217     if (data == NULL)
218       break;
219     tvh_safe_usleep((long)data->delay_ms * 1000);
220     upnp_dump_data(data);
221     udp_write_queue(unicast, &data->queue, &data->storage);
222     htsbuf_queue_flush(&data->queue);
223     free(data);
224   }
225 
226 error:
227   atomic_set(&upnp_running, 0);
228   tvhpoll_destroy(poll);
229   udp_close(unicast);
230   udp_close(multicast);
231   return NULL;
232 }
233 
234 /*
235  *  Fire up UPnP server
236  */
237 void
upnp_server_init(const char * bindaddr)238 upnp_server_init(const char *bindaddr)
239 {
240   int r;
241 
242   memset(&upnp_ipv4_multicast, 0, sizeof(upnp_ipv4_multicast));
243   upnp_ipv4_multicast.ss_family       = AF_INET;
244   IP_AS_V4(upnp_ipv4_multicast, port) = htons(1900);
245   r = inet_pton(AF_INET, "239.255.255.250", &IP_AS_V4(upnp_ipv4_multicast, addr));
246   assert(r);
247 
248   pthread_mutex_init(&upnp_lock, NULL);
249   TAILQ_INIT(&upnp_data_write);
250   TAILQ_INIT(&upnp_services);
251   atomic_set(&upnp_running, 1);
252   tvhthread_create(&upnp_tid, NULL, upnp_thread, (char *)bindaddr, "upnp");
253 }
254 
255 void
upnp_server_done(void)256 upnp_server_done(void)
257 {
258   upnp_data_t *data;
259   upnp_service_t *us;
260 
261   atomic_set(&upnp_running, 0);
262   pthread_kill(upnp_tid, SIGTERM);
263   pthread_join(upnp_tid, NULL);
264   while ((us = TAILQ_FIRST(&upnp_services)) != NULL)
265     upnp_service_destroy(us);
266   while ((data = TAILQ_FIRST(&upnp_data_write)) != NULL) {
267     TAILQ_REMOVE(&upnp_data_write, data, data_link);
268     htsbuf_queue_flush(&data->queue);
269     free(data);
270   }
271 }
272