1 /* MiniDLNA media server
2 * Copyright (C) 2013-2017 NETGEAR
3 *
4 * This file is part of MiniDLNA.
5 *
6 * MiniDLNA is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * MiniDLNA 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 MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include <stdio.h>
19 #include <string.h>
20 #include <time.h>
21
22 #include "clients.h"
23 #include "event.h"
24 #include "getifaddr.h"
25 #include "log.h"
26
27 struct client_type_s client_types[] =
28 {
29 { 0,
30 0,
31 "Unknown",
32 NULL,
33 EMatchNone
34 },
35
36 { EXbox,
37 FLAG_MIME_AVI_AVI | FLAG_MS_PFS,
38 "Xbox 360",
39 "Xbox/",
40 EUserAgent
41 },
42
43 { EPS3,
44 FLAG_DLNA | FLAG_MIME_AVI_DIVX,
45 "PLAYSTATION 3",
46 "PLAYSTATION",
47 EUserAgent
48 },
49
50 { EPS3,
51 FLAG_DLNA | FLAG_MIME_AVI_DIVX,
52 "PLAYSTATION 3",
53 "PLAYSTATION 3",
54 EXAVClientInfo
55 },
56
57 /* User-Agent: DLNADOC/1.50 SEC_HHP_[PC]LPC001/1.0 MS-DeviceCaps/1024 */
58 /* This is AllShare running on a PC. We don't want to respond with Samsung
59 * capabilities, or Windows (and AllShare) might get grumpy. */
60 { 0,
61 FLAG_DLNA,
62 "AllShare",
63 "SEC_HHP_[PC]",
64 EUserAgent
65 },
66
67 { ESamsungBDJ5500,
68 FLAG_SAMSUNG | FLAG_DLNA | FLAG_NO_RESIZE | FLAG_CAPTION_RES | FLAG_SKIP_DLNA_PN,
69 "Samsung BD J5500",
70 "[BD]J5500",
71 EUserAgent
72 },
73
74 /* Samsung Series [CDE] BDPs and TVs must be separated, or some of our
75 * advertised extra features trigger a folder browsing bug on BDPs. */
76 /* User-Agent: DLNADOC/1.50 SEC_HHP_BD-D5100/1.0 */
77 { ESamsungSeriesCDEBDP,
78 FLAG_SAMSUNG | FLAG_DLNA | FLAG_NO_RESIZE,
79 "Samsung Series [CDEF] BDP",
80 "SEC_HHP_BD",
81 EUserAgent
82 },
83
84 /* Samsung Series [Q] TVs work wit milliseconds for bookmarks */
85 /* User-Agent: DLNADOC/1.50 SEC_HHP_[TV] Samsung Q7 Series (49)/1.0 */
86 { ESamsungSeriesQ,
87 FLAG_SAMSUNG | FLAG_DLNA | FLAG_NO_RESIZE | FLAG_SAMSUNG_DCM10 | FLAG_CAPTION_RES | FLAG_CONVERT_MS,
88 "Samsung Series [Q]",
89 "SEC_HHP_[TV] Samsung Q",
90 EUserAgent
91 },
92
93 /* User-Agent: DLNADOC/1.50 SEC_HHP_[TV]UE40D7000/1.0 */
94 /* User-Agent: DLNADOC/1.50 SEC_HHP_ Family TV/1.0 */
95 /* USER-AGENT: DLNADOC/1.50 SEC_HHP_[TV] UE65JU7000/1.0 UPnP/1.0 */
96 { ESamsungSeriesCDE,
97 FLAG_SAMSUNG | FLAG_DLNA | FLAG_NO_RESIZE | FLAG_SAMSUNG_DCM10 | FLAG_CAPTION_RES,
98 "Samsung Series [CDEFJ]",
99 "SEC_HHP_",
100 EUserAgent
101 },
102
103 { ESamsungSeriesA,
104 FLAG_SAMSUNG | FLAG_DLNA | FLAG_NO_RESIZE,
105 "Samsung Series A",
106 "SamsungWiselinkPro",
107 EUserAgent
108 },
109
110 { ESamsungSeriesB,
111 FLAG_SAMSUNG | FLAG_DLNA | FLAG_NO_RESIZE,
112 "Samsung Series B",
113 "Samsung DTV DMR",
114 EModelName
115 },
116
117 /* User-Agent: Panasonic MIL DLNA CP UPnP/1.0 DLNADOC/1.50 */
118 { EPanasonic,
119 FLAG_DLNA | FLAG_FORCE_SORT,
120 "Panasonic",
121 "Panasonic",
122 EUserAgent
123 },
124
125 /* User-Agent: IPI/1.0 UPnP/1.0 DLNADOC/1.50 */
126 { ENetFrontLivingConnect,
127 FLAG_DLNA | FLAG_FORCE_SORT | FLAG_CAPTION_RES,
128 "NetFront Living Connect",
129 "IPI/1",
130 EUserAgent
131 },
132
133 { EDenonReceiver,
134 FLAG_DLNA,
135 "Denon Receiver",
136 "bridgeCo-DMP/3",
137 EUserAgent
138 },
139
140 { EFreeBox,
141 FLAG_RESIZE_THUMBS,
142 "FreeBox",
143 "fbxupnpav/",
144 EUserAgent
145 },
146
147 { EPopcornHour,
148 FLAG_MIME_FLAC_FLAC,
149 "Popcorn Hour",
150 "SMP8634",
151 EUserAgent
152 },
153
154 /* X-AV-Client-Info: av=5.0; cn="Sony Corporation"; mn="Blu-ray Disc Player"; mv="2.0" */
155 /* X-AV-Client-Info: av=5.0; cn="Sony Corporation"; mn="BLU-RAY HOME THEATRE SYSTEM"; mv="2.0"; */
156 /* Sony SMP-100 needs the same treatment as their BDP-S370 */
157 /* X-AV-Client-Info: av=5.0; cn="Sony Corporation"; mn="Media Player"; mv="2.0" */
158 { ESonyBDP,
159 FLAG_DLNA,
160 "Sony BDP",
161 "mv=\"2.0\"",
162 EXAVClientInfo
163 },
164
165 /* USER-AGENT: Linux/2.6.35 UPnP/1.0 DLNADOC/1.50 INTEL_NMPR/2.0 LGE_DLNA_SDK/1.6.0 */
166 { ELGNetCastDevice,
167 FLAG_DLNA | FLAG_CAPTION_RES | FLAG_MIME_FLAC_FLAC,
168 "LG",
169 "LGE_DLNA_SDK/1.6.0",
170 EUserAgent
171 },
172
173 /* User-Agent: Linux/2.6.31-1.0 UPnP/1.0 DLNADOC/1.50 INTEL_NMPR/2.0 LGE_DLNA_SDK/1.5.0 */
174 { ELGDevice,
175 FLAG_DLNA | FLAG_CAPTION_RES | FLAG_MIME_FLAC_FLAC,
176 "LG",
177 "LGE_DLNA_SDK",
178 EUserAgent
179 },
180
181 /* X-AV-Client-Info: av=5.0; cn="Sony Corporation"; mn="BRAVIA KDL-40EX503"; mv="1.7"; */
182 { ESonyBravia,
183 FLAG_DLNA,
184 "Sony Bravia",
185 "BRAVIA",
186 EXAVClientInfo
187 },
188
189 /* X-AV-Client-Info: av=5.0; hn=""; cn="Sony Corporation"; mn="INTERNET TV NSX-40GT 1"; mv="0.1"; */
190 { ESonyInternetTV,
191 FLAG_DLNA,
192 "Sony Internet TV",
193 "INTERNET TV",
194 EXAVClientInfo
195 },
196
197 { ENetgearEVA2000,
198 FLAG_MS_PFS | FLAG_RESIZE_THUMBS,
199 "EVA2000",
200 "Verismo,",
201 EUserAgent
202 },
203
204 { EDirecTV,
205 FLAG_RESIZE_THUMBS,
206 "DirecTV",
207 "DIRECTV ",
208 EUserAgent
209 },
210
211 { EToshibaTV,
212 FLAG_DLNA,
213 "Toshiba TV",
214 "UPnP/1.0 DLNADOC/1.50 Intel_SDK_for_UPnP_devices/1.2",
215 EUserAgent
216 },
217
218 { EHyundaiTV,
219 FLAG_DLNA,
220 "Hyundai TV",
221 "HYUNDAITV",
222 EFriendlyName
223 },
224
225 { ERokuSoundBridge,
226 FLAG_MS_PFS | FLAG_AUDIO_ONLY | FLAG_MIME_WAV_WAV | FLAG_FORCE_SORT,
227 "Roku SoundBridge",
228 "Roku SoundBridge",
229 EModelName
230 },
231
232 { EMarantzDMP,
233 FLAG_DLNA | FLAG_MIME_WAV_WAV,
234 "marantz DMP",
235 "marantz DMP",
236 EFriendlyNameSSDP
237 },
238
239 { EMediaRoom,
240 FLAG_MS_PFS,
241 "MS MediaRoom",
242 "Microsoft-IPTV-Client",
243 EUserAgent
244 },
245
246 { ELifeTab,
247 FLAG_MS_PFS,
248 "LIFETAB",
249 "LIFETAB",
250 EFriendlyName
251 },
252
253 { EAsusOPlay,
254 FLAG_DLNA | FLAG_MIME_AVI_AVI | FLAG_CAPTION_RES,
255 "Asus OPlay Mini/Mini+",
256 "O!Play",
257 EUserAgent
258 },
259
260 { EBubbleUPnP,
261 FLAG_CAPTION_RES,
262 "BubbleUPnP",
263 "BubbleUPnP",
264 EUserAgent
265 },
266
267 { EMovian,
268 FLAG_CAPTION_RES,
269 "Movian",
270 "Movian",
271 EUserAgent
272 },
273
274 { EKodi,
275 FLAG_DLNA | FLAG_MIME_AVI_AVI | FLAG_CAPTION_RES,
276 "Kodi",
277 "Kodi",
278 EUserAgent
279 },
280
281 { 0,
282 FLAG_DLNA | FLAG_MIME_AVI_AVI,
283 "Windows",
284 "FDSSDP",
285 EUserAgent
286 },
287
288 { 0,
289 0,
290 "TiVo",
291 "TvHttpClient",
292 EUserAgent
293 },
294
295 { EStandardDLNA150,
296 FLAG_DLNA | FLAG_MIME_AVI_AVI,
297 "Generic DLNA 1.5",
298 "DLNADOC/1.50",
299 EUserAgent
300 },
301
302 { EStandardUPnP,
303 0,
304 "Generic UPnP 1.0",
305 "UPnP/1.0",
306 EUserAgent
307 },
308
309 { 0, 0, NULL, 0 }
310 };
311
312 struct client_cache_s clients[CLIENT_CACHE_SLOTS];
313
314 struct client_cache_s *
SearchClientCache(struct in_addr addr,int quiet)315 SearchClientCache(struct in_addr addr, int quiet)
316 {
317 int i;
318
319 for (i = 0; i < CLIENT_CACHE_SLOTS; i++)
320 {
321 if (clients[i].addr.s_addr == addr.s_addr)
322 {
323 /* Invalidate this client cache if it's older than 1 hour */
324 if ((time(NULL) - clients[i].age) > 3600)
325 {
326 unsigned char mac[6];
327 if (get_remote_mac(addr, mac) == 0 &&
328 memcmp(mac, clients[i].mac, 6) == 0)
329 {
330 /* Same MAC as last time when we were able to identify the client,
331 * so extend the timeout by another hour. */
332 clients[i].age = time(NULL);
333 }
334 else
335 {
336 memset(&clients[i], 0, sizeof(struct client_cache_s));
337 return NULL;
338 }
339 }
340 if (!quiet)
341 DPRINTF(E_DEBUG, L_HTTP, "Client found in cache. [%s/entry %d]\n",
342 clients[i].type->name, i);
343 return &clients[i];
344 }
345 }
346
347 return NULL;
348 }
349
350 struct client_cache_s *
AddClientCache(struct in_addr addr,int type)351 AddClientCache(struct in_addr addr, int type)
352 {
353 int i;
354
355 for (i = 0; i < CLIENT_CACHE_SLOTS; i++)
356 {
357 if (clients[i].addr.s_addr)
358 continue;
359 get_remote_mac(addr, clients[i].mac);
360 clients[i].addr = addr;
361 clients[i].type = &client_types[type];
362 clients[i].age = time(NULL);
363 DPRINTF(E_DEBUG, L_HTTP, "Added client [%s/%s/%02X:%02X:%02X:%02X:%02X:%02X] to cache slot %d.\n",
364 client_types[type].name, inet_ntoa(clients[i].addr),
365 clients[i].mac[0], clients[i].mac[1], clients[i].mac[2],
366 clients[i].mac[3], clients[i].mac[4], clients[i].mac[5], i);
367 return &clients[i];
368 }
369
370 return NULL;
371 }
372