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