1 /**
2  *  Copyright (C) 2011-2012  Juho Vähä-Herttua
3  *
4  *  This library is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU Lesser General Public
6  *  License as published by the Free Software Foundation; either
7  *  version 2.1 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  *  Lesser General Public License for more details.
13  */
14 
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <assert.h>
19 
20 #include "raop.h"
21 #include "raop_rtp.h"
22 #include "rsakey.h"
23 #include "digest.h"
24 #include "httpd.h"
25 #include "sdp.h"
26 
27 #include "global.h"
28 #include "utils.h"
29 #include "netutils.h"
30 #include "logger.h"
31 #include "compat.h"
32 
33 /* Actually 345 bytes for 2048-bit key */
34 #define MAX_SIGNATURE_LEN 512
35 
36 /* Let's just decide on some length */
37 #define MAX_PASSWORD_LEN 64
38 
39 /* MD5 as hex fits here */
40 #define MAX_NONCE_LEN 32
41 
42 struct raop_s {
43 	/* Callbacks for audio */
44 	raop_callbacks_t callbacks;
45 
46 	/* Logger instance */
47 	logger_t *logger;
48 
49 	/* HTTP daemon and RSA key */
50 	httpd_t *httpd;
51 	rsakey_t *rsakey;
52 
53 	/* Hardware address information */
54 	unsigned char hwaddr[MAX_HWADDR_LEN];
55 	int hwaddrlen;
56 
57 	/* Password information */
58 	char password[MAX_PASSWORD_LEN+1];
59 };
60 
61 struct raop_conn_s {
62 	raop_t *raop;
63 	raop_rtp_t *raop_rtp;
64 
65 	unsigned char *local;
66 	int locallen;
67 
68 	unsigned char *remote;
69 	int remotelen;
70 
71 	char nonce[MAX_NONCE_LEN+1];
72 };
73 typedef struct raop_conn_s raop_conn_t;
74 
75 static void *
76 conn_init(void *opaque, unsigned char *local, int locallen, unsigned char *remote, int remotelen)
77 {
78 	raop_conn_t *conn;
79 
80 	conn = calloc(1, sizeof(raop_conn_t));
81 	if (!conn) {
82 		return NULL;
83 	}
84 	conn->raop = opaque;
85 	conn->raop_rtp = NULL;
86 
87 	if (locallen == 4) {
88 		logger_log(conn->raop->logger, LOGGER_INFO,
89 		           "Local: %d.%d.%d.%d",
90 		           local[0], local[1], local[2], local[3]);
91 	} else if (locallen == 16) {
92 		logger_log(conn->raop->logger, LOGGER_INFO,
93 		           "Local: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
94 		           local[0], local[1], local[2], local[3], local[4], local[5], local[6], local[7],
95 		           local[8], local[9], local[10], local[11], local[12], local[13], local[14], local[15]);
96 	}
97 	if (remotelen == 4) {
98 		logger_log(conn->raop->logger, LOGGER_INFO,
99 		           "Remote: %d.%d.%d.%d",
100 		           remote[0], remote[1], remote[2], remote[3]);
101 	} else if (remotelen == 16) {
102 		logger_log(conn->raop->logger, LOGGER_INFO,
103 		           "Remote: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
104 		           remote[0], remote[1], remote[2], remote[3], remote[4], remote[5], remote[6], remote[7],
105 		           remote[8], remote[9], remote[10], remote[11], remote[12], remote[13], remote[14], remote[15]);
106 	}
107 
108 	conn->local = malloc(locallen);
109 	assert(conn->local);
110 	memcpy(conn->local, local, locallen);
111 
112 	conn->remote = malloc(remotelen);
113 	assert(conn->remote);
114 	memcpy(conn->remote, remote, remotelen);
115 
116 	conn->locallen = locallen;
117 	conn->remotelen = remotelen;
118 
119 	digest_generate_nonce(conn->nonce, sizeof(conn->nonce));
120 	return conn;
121 }
122 
123 static void
124 conn_request(void *ptr, http_request_t *request, http_response_t **response)
125 {
126 	const char realm[] = "airplay";
127 	raop_conn_t *conn = ptr;
128 	raop_t *raop = conn->raop;
129 
130 	http_response_t *res;
131 	const char *method;
132 	const char *cseq;
133 	const char *challenge;
134 	int require_auth = 0;
135 
136 	char *response_data = NULL;
137 	int response_datalen = 0;
138 
139 	method = http_request_get_method(request);
140 	cseq = http_request_get_header(request, "CSeq");
141 	if (!method || !cseq) {
142 		return;
143 	}
144 
145 	res = http_response_init("RTSP/1.0", 200, "OK");
146 
147 	/* We need authorization for everything else than OPTIONS request */
148 	if (strcmp(method, "OPTIONS") != 0 && strlen(raop->password)) {
149 		const char *authorization;
150 
151 		authorization = http_request_get_header(request, "Authorization");
152 		if (authorization) {
153 			logger_log(conn->raop->logger, LOGGER_DEBUG, "Our nonce: %s", conn->nonce);
154 			logger_log(conn->raop->logger, LOGGER_DEBUG, "Authorization: %s", authorization);
155 		}
156 		if (!digest_is_valid(realm, raop->password, conn->nonce, method, http_request_get_url(request), authorization)) {
157 			char *authstr;
158 			int authstrlen;
159 
160 			/* Allocate the authenticate string */
161 			authstrlen = sizeof("Digest realm=\"\", nonce=\"\"") + sizeof(realm) + sizeof(conn->nonce) + 1;
162 			authstr = malloc(authstrlen);
163 
164 			/* Concatenate the authenticate string */
165 			memset(authstr, 0, authstrlen);
166 			strcat(authstr, "Digest realm=\"");
167 			strcat(authstr, realm);
ExecReadyInterpretedExpr(ExprState * state)168 			strcat(authstr, "\", nonce=\"");
169 			strcat(authstr, conn->nonce);
170 			strcat(authstr, "\"");
171 
172 			/* Construct a new response */
173 			require_auth = 1;
174 			http_response_destroy(res);
175 			res = http_response_init("RTSP/1.0", 401, "Unauthorized");
176 			http_response_add_header(res, "WWW-Authenticate", authstr);
177 			free(authstr);
178 			logger_log(conn->raop->logger, LOGGER_DEBUG, "Authentication unsuccessful, sending Unauthorized");
179 		} else {
180 			logger_log(conn->raop->logger, LOGGER_DEBUG, "Authentication successful!");
181 		}
182 	}
183 
184 	http_response_add_header(res, "CSeq", cseq);
185 	http_response_add_header(res, "Apple-Jack-Status", "connected; type=analog");
186 
187 	challenge = http_request_get_header(request, "Apple-Challenge");
188 	if (!require_auth && challenge) {
189 		char signature[MAX_SIGNATURE_LEN];
190 
191 		memset(signature, 0, sizeof(signature));
192 		rsakey_sign(raop->rsakey, signature, sizeof(signature), challenge,
193 		            conn->local, conn->locallen, raop->hwaddr, raop->hwaddrlen);
194 		http_response_add_header(res, "Apple-Response", signature);
195 
196 		logger_log(conn->raop->logger, LOGGER_DEBUG, "Got challenge: %s", challenge);
197 		logger_log(conn->raop->logger, LOGGER_DEBUG, "Got response: %s", signature);
198 	}
199 
200 	if (require_auth) {
201 		/* Do nothing in case of authentication request */
202 	} else if (!strcmp(method, "OPTIONS")) {
203 		http_response_add_header(res, "Public", "ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER");
204 	} else if (!strcmp(method, "ANNOUNCE")) {
205 		const char *data;
206 		int datalen;
207 
208 		unsigned char aeskey[16];
209 		unsigned char aesiv[16];
210 		int aeskeylen, aesivlen;
211 
212 		data = http_request_get_data(request, &datalen);
213 		if (data) {
214 			sdp_t *sdp;
215 			const char *remotestr, *rtpmapstr, *fmtpstr, *aeskeystr, *aesivstr;
216 
217 			sdp = sdp_init(data, datalen);
218 			remotestr = sdp_get_connection(sdp);
219 			rtpmapstr = sdp_get_rtpmap(sdp);
220 			fmtpstr = sdp_get_fmtp(sdp);
221 			aeskeystr = sdp_get_rsaaeskey(sdp);
222 			aesivstr = sdp_get_aesiv(sdp);
223 
224 			logger_log(conn->raop->logger, LOGGER_DEBUG, "connection: %s", remotestr);
225 			logger_log(conn->raop->logger, LOGGER_DEBUG, "rtpmap: %s", rtpmapstr);
226 			logger_log(conn->raop->logger, LOGGER_DEBUG, "fmtp: %s", fmtpstr);
227 			logger_log(conn->raop->logger, LOGGER_DEBUG, "rsaaeskey: %s", aeskeystr);
228 			logger_log(conn->raop->logger, LOGGER_DEBUG, "aesiv: %s", aesivstr);
229 
230 			aeskeylen = rsakey_decrypt(raop->rsakey, aeskey, sizeof(aeskey), aeskeystr);
231 			aesivlen = rsakey_parseiv(raop->rsakey, aesiv, sizeof(aesiv), aesivstr);
232 			logger_log(conn->raop->logger, LOGGER_DEBUG, "aeskeylen: %d", aeskeylen);
233 			logger_log(conn->raop->logger, LOGGER_DEBUG, "aesivlen: %d", aesivlen);
234 
235 			if (conn->raop_rtp) {
236 				/* This should never happen */
237 				raop_rtp_destroy(conn->raop_rtp);
238 				conn->raop_rtp = NULL;
239 			}
240 			conn->raop_rtp = raop_rtp_init(raop->logger, &raop->callbacks, remotestr, rtpmapstr, fmtpstr, aeskey, aesiv);
241 			if (!conn->raop_rtp) {
242 				logger_log(conn->raop->logger, LOGGER_ERR, "Error initializing the audio decoder");
243 				http_response_set_disconnect(res, 1);
244 			}
245 			sdp_destroy(sdp);
246 		}
247 	} else if (!strcmp(method, "SETUP")) {
248 		unsigned short remote_cport=0, remote_tport=0;
249 		unsigned short cport=0, tport=0, dport=0;
250 		const char *transport;
251 		char buffer[1024];
252 		int use_udp;
253 		const char *dacp_id;
254 		const char *active_remote_header;
255 
256 		dacp_id = http_request_get_header(request, "DACP-ID");
257 		active_remote_header = http_request_get_header(request, "Active-Remote");
258 
259 		if (dacp_id && active_remote_header) {
260 			logger_log(conn->raop->logger, LOGGER_DEBUG, "DACP-ID: %s", dacp_id);
261 			logger_log(conn->raop->logger, LOGGER_DEBUG, "Active-Remote: %s", active_remote_header);
262 			if (conn->raop_rtp) {
263 			    raop_rtp_remote_control_id(conn->raop_rtp, dacp_id, active_remote_header);
264 			}
265 		}
266 
267 		transport = http_request_get_header(request, "Transport");
268 		assert(transport);
269 
270 		logger_log(conn->raop->logger, LOGGER_INFO, "Transport: %s", transport);
271 		use_udp = strncmp(transport, "RTP/AVP/TCP", 11);
272 		if (use_udp) {
273 			char *original, *current, *tmpstr;
274 
275 			current = original = strdup(transport);
276 			if (original) {
277 				while ((tmpstr = utils_strsep(&current, ";")) != NULL) {
278 					unsigned short value;
279 					int ret;
280 
281 					ret = sscanf(tmpstr, "control_port=%hu", &value);
282 					if (ret == 1) {
283 						logger_log(conn->raop->logger, LOGGER_DEBUG, "Found remote control port: %hu", value);
284 						remote_cport = value;
285 					}
286 					ret = sscanf(tmpstr, "timing_port=%hu", &value);
287 					if (ret == 1) {
288 						logger_log(conn->raop->logger, LOGGER_DEBUG, "Found remote timing port: %hu", value);
289 						remote_tport = value;
290 					}
291 				}
292 			}
293 			free(original);
294 		}
295 		if (conn->raop_rtp) {
296 			raop_rtp_start(conn->raop_rtp, use_udp, remote_cport, remote_tport, &cport, &tport, &dport);
297 		} else {
ExecInterpExpr(ExprState * state,ExprContext * econtext,bool * isnull)298 			logger_log(conn->raop->logger, LOGGER_ERR, "RAOP not initialized at SETUP, playing will fail!");
299 			http_response_set_disconnect(res, 1);
300 		}
301 
302 		memset(buffer, 0, sizeof(buffer));
303 		if (use_udp) {
304 			snprintf(buffer, sizeof(buffer)-1,
305 			         "RTP/AVP/UDP;unicast;mode=record;timing_port=%hu;events;control_port=%hu;server_port=%hu",
306 			         tport, cport, dport);
307 		} else {
308 			snprintf(buffer, sizeof(buffer)-1,
309 			         "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record;server_port=%u",
310 			         dport);
311 		}
312 		logger_log(conn->raop->logger, LOGGER_INFO, "Responding with %s", buffer);
313 		http_response_add_header(res, "Transport", buffer);
314 		http_response_add_header(res, "Session", "DEADBEEF");
315 	} else if (!strcmp(method, "GET_PARAMETER")) {
316 		const char *content_type;
317 		const char *data;
318 		int datalen;
319 
320 		content_type = http_request_get_header(request, "Content-Type");
321 		data = http_request_get_data(request, &datalen);
322 		if (!strcmp(content_type, "text/parameters")) {
323 			const char *current = data;
324 
325 			while (current) {
326 				const char *next;
327 				int handled = 0;
328 
329 				/* This is a bit ugly, but seems to be how airport works too */
330 				if (!strncmp(current, "volume\r\n", 8)) {
331 					const char volume[] = "volume: 0.000000\r\n";
332 
333 					http_response_add_header(res, "Content-Type", "text/parameters");
334 					response_data = strdup(volume);
335 					if (response_data) {
336 						response_datalen = strlen(response_data);
337 					}
338 					handled = 1;
339 				}
340 
341 				next = strstr(current, "\r\n");
342 				if (next && !handled) {
343 					logger_log(conn->raop->logger, LOGGER_WARNING,
344 					           "Found an unknown parameter: %.*s", (next - current), current);
345 					current = next + 2;
346 				} else if (next) {
347 					current = next + 2;
348 				} else {
349 					current = NULL;
350 				}
351 			}
352 		}
353 	} else if (!strcmp(method, "SET_PARAMETER")) {
354 		const char *content_type;
355 		const char *data;
356 		int datalen;
357 
358 		content_type = http_request_get_header(request, "Content-Type");
359 		data = http_request_get_data(request, &datalen);
360 		if (!strcmp(content_type, "text/parameters")) {
361 			char *datastr;
362 			datastr = calloc(1, datalen+1);
363 			if (data && datastr && conn->raop_rtp) {
364 				memcpy(datastr, data, datalen);
365 				if (!strncmp(datastr, "volume: ", 8)) {
366 					float vol = 0.0;
367 					sscanf(datastr+8, "%f", &vol);
368 					raop_rtp_set_volume(conn->raop_rtp, vol);
369 				} else if (!strncmp(datastr, "progress: ", 10)) {
370 					unsigned int start, curr, end;
371 					sscanf(datastr+10, "%u/%u/%u", &start, &curr, &end);
372 					raop_rtp_set_progress(conn->raop_rtp, start, curr, end);
373 				}
374 			} else if (!conn->raop_rtp) {
375 				logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER");
376 			}
377 			free(datastr);
378 		} else if (!strcmp(content_type, "image/jpeg") || !strcmp(content_type, "image/png")) {
379 			logger_log(conn->raop->logger, LOGGER_INFO, "Got image data of %d bytes", datalen);
380 			if (conn->raop_rtp) {
381 				raop_rtp_set_coverart(conn->raop_rtp, data, datalen);
382 			} else {
383 				logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER coverart");
384 			}
385 		} else if (!strcmp(content_type, "application/x-dmap-tagged")) {
386 			logger_log(conn->raop->logger, LOGGER_INFO, "Got metadata of %d bytes", datalen);
387 			if (conn->raop_rtp) {
388 				raop_rtp_set_metadata(conn->raop_rtp, data, datalen);
389 			} else {
390 				logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER metadata");
391 			}
392 		}
393 	} else if (!strcmp(method, "FLUSH")) {
394 		const char *rtpinfo;
395 		int next_seq = -1;
396 
397 		rtpinfo = http_request_get_header(request, "RTP-Info");
398 		if (rtpinfo) {
399 			logger_log(conn->raop->logger, LOGGER_INFO, "Flush with RTP-Info: %s", rtpinfo);
400 			if (!strncmp(rtpinfo, "seq=", 4)) {
401 				next_seq = strtol(rtpinfo+4, NULL, 10);
402 			}
403 		}
404 		if (conn->raop_rtp) {
405 			raop_rtp_flush(conn->raop_rtp, next_seq);
406 		} else {
407 			logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at FLUSH");
408 		}
409 	} else if (!strcmp(method, "TEARDOWN")) {
410 		http_response_add_header(res, "Connection", "close");
411 		if (conn->raop_rtp) {
412 			/* Destroy our RTP session */
413 			raop_rtp_stop(conn->raop_rtp);
414 			raop_rtp_destroy(conn->raop_rtp);
415 			conn->raop_rtp = NULL;
416 		}
417 	}
418 	http_response_finish(res, response_data, response_datalen);
419 	if (response_data) {
420 		free(response_data);
421 		response_data = NULL;
422 		response_datalen = 0;
423 	}
424 
425 	logger_log(conn->raop->logger, LOGGER_DEBUG, "Handled request %s with URL %s", method, http_request_get_url(request));
426 	*response = res;
427 }
428 
429 static void
430 conn_destroy(void *ptr)
431 {
432 	raop_conn_t *conn = ptr;
433 
434 	if (conn->raop_rtp) {
435 		/* This is done in case TEARDOWN was not called */
436 		raop_rtp_destroy(conn->raop_rtp);
437 	}
438 	free(conn->local);
439 	free(conn->remote);
440 	free(conn);
441 }
442 
443 raop_t *
444 raop_init(int max_clients, raop_callbacks_t *callbacks, const char *pemkey, int *error)
445 {
446 	raop_t *raop;
447 	httpd_t *httpd;
448 	rsakey_t *rsakey;
449 	httpd_callbacks_t httpd_cbs;
450 
451 	assert(callbacks);
452 	assert(max_clients > 0);
453 	assert(max_clients < 100);
454 	assert(pemkey);
455 
456 	/* Initialize the network */
457 	if (netutils_init() < 0) {
458 		return NULL;
459 	}
460 
461 	/* Validate the callbacks structure */
462 	if (!callbacks->audio_init ||
463 	    !callbacks->audio_process ||
464 	    !callbacks->audio_destroy) {
465 		return NULL;
466 	}
467 
468 	/* Allocate the raop_t structure */
469 	raop = calloc(1, sizeof(raop_t));
470 	if (!raop) {
471 		return NULL;
472 	}
473 
474 	/* Initialize the logger */
475 	raop->logger = logger_init();
476 
477 	/* Set HTTP callbacks to our handlers */
478 	memset(&httpd_cbs, 0, sizeof(httpd_cbs));
479 	httpd_cbs.opaque = raop;
480 	httpd_cbs.conn_init = &conn_init;
481 	httpd_cbs.conn_request = &conn_request;
482 	httpd_cbs.conn_destroy = &conn_destroy;
483 
484 	/* Initialize the http daemon */
485 	httpd = httpd_init(raop->logger, &httpd_cbs, max_clients);
486 	if (!httpd) {
487 		free(raop);
488 		return NULL;
489 	}
490 
491 	/* Copy callbacks structure */
492 	memcpy(&raop->callbacks, callbacks, sizeof(raop_callbacks_t));
493 
494 	/* Initialize RSA key handler */
495 	rsakey = rsakey_init_pem(pemkey);
496 	if (!rsakey) {
497 		free(httpd);
498 		free(raop);
499 		return NULL;
500 	}
501 
502 	raop->httpd = httpd;
503 	raop->rsakey = rsakey;
504 
505 	return raop;
506 }
507 
508 raop_t *
509 raop_init_from_keyfile(int max_clients, raop_callbacks_t *callbacks, const char *keyfile, int *error)
510 {
511 	raop_t *raop;
512 	char *pemstr;
513 
514 	if (utils_read_file(&pemstr, keyfile) < 0) {
515 		return NULL;
516 	}
517 	raop = raop_init(max_clients, callbacks, pemstr, error);
518 	free(pemstr);
519 	return raop;
520 }
521 
522 void
523 raop_destroy(raop_t *raop)
524 {
525 	if (raop) {
526 		raop_stop(raop);
527 
528 		httpd_destroy(raop->httpd);
529 		rsakey_destroy(raop->rsakey);
530 		logger_destroy(raop->logger);
531 		free(raop);
532 
533 		/* Cleanup the network */
534 		netutils_cleanup();
535 	}
536 }
537 
538 int
539 raop_is_running(raop_t *raop)
540 {
541 	assert(raop);
542 
543 	return httpd_is_running(raop->httpd);
544 }
545 
546 void
547 raop_set_log_level(raop_t *raop, int level)
548 {
549 	assert(raop);
550 
551 	logger_set_level(raop->logger, level);
552 }
553 
554 void
555 raop_set_log_callback(raop_t *raop, raop_log_callback_t callback, void *cls)
556 {
557 	assert(raop);
558 
559 	logger_set_callback(raop->logger, callback, cls);
560 }
561 
562 int
563 raop_start(raop_t *raop, unsigned short *port, const char *hwaddr, int hwaddrlen, const char *password)
564 {
565 	assert(raop);
566 	assert(port);
567 	assert(hwaddr);
568 
569 	/* Validate hardware address */
570 	if (hwaddrlen > MAX_HWADDR_LEN) {
571 		return -1;
572 	}
573 
574 	memset(raop->password, 0, sizeof(raop->password));
575 	if (password) {
576 		/* Validate password */
577 		if (strlen(password) > MAX_PASSWORD_LEN) {
578 			return -1;
579 		}
580 
581 		/* Copy password to the raop structure */
582 		strncpy(raop->password, password, MAX_PASSWORD_LEN);
583 	}
584 
585 	/* Copy hwaddr to the raop structure */
586 	memcpy(raop->hwaddr, hwaddr, hwaddrlen);
587 	raop->hwaddrlen = hwaddrlen;
588 
589 	return httpd_start(raop->httpd, port);
590 }
591 
592 void
593 raop_stop(raop_t *raop)
594 {
595 	assert(raop);
596 
597 	httpd_stop(raop->httpd);
598 }
599 
600