2 * Copyright (C) 2011-2012 Juho Vähä-Herttua
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.
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.
31 /* Actually 345 bytes for 2048-bit key */
32 #define MAX_SIGNATURE_LEN 512
35 /* Callbacks for audio */
36 raop_callbacks_t callbacks
;
41 /* HTTP daemon and RSA key */
45 /* Hardware address information */
46 unsigned char hwaddr
[MAX_HWADDR_LEN
];
57 unsigned char *remote
;
60 typedef struct raop_conn_s raop_conn_t
;
63 conn_init(void *opaque
, unsigned char *local
, int locallen
, unsigned char *remote
, int remotelen
)
67 conn
= calloc(1, sizeof(raop_conn_t
));
72 conn
->raop_rtp
= NULL
;
75 logger_log(&conn
->raop
->logger
, LOGGER_INFO
,
76 "Local: %d.%d.%d.%d\n",
77 local
[0], local
[1], local
[2], local
[3]);
78 } else if (locallen
== 16) {
79 logger_log(&conn
->raop
->logger
, LOGGER_INFO
,
80 "Local: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x\n",
81 local
[0], local
[1], local
[2], local
[3], local
[4], local
[5], local
[6], local
[7],
82 local
[8], local
[9], local
[10], local
[11], local
[12], local
[13], local
[14], local
[15]);
85 logger_log(&conn
->raop
->logger
, LOGGER_INFO
,
86 "Remote: %d.%d.%d.%d\n",
87 remote
[0], remote
[1], remote
[2], remote
[3]);
88 } else if (remotelen
== 16) {
89 logger_log(&conn
->raop
->logger
, LOGGER_INFO
,
90 "Remote: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x\n",
91 remote
[0], remote
[1], remote
[2], remote
[3], remote
[4], remote
[5], remote
[6], remote
[7],
92 remote
[8], remote
[9], remote
[10], remote
[11], remote
[12], remote
[13], remote
[14], remote
[15]);
95 conn
->local
= malloc(locallen
);
97 memcpy(conn
->local
, local
, locallen
);
99 conn
->remote
= malloc(remotelen
);
100 assert(conn
->remote
);
101 memcpy(conn
->remote
, remote
, remotelen
);
103 conn
->locallen
= locallen
;
104 conn
->remotelen
= remotelen
;
109 conn_request(void *ptr
, http_request_t
*request
, http_response_t
**response
)
111 raop_conn_t
*conn
= ptr
;
112 raop_t
*raop
= conn
->raop
;
114 http_response_t
*res
;
117 const char *challenge
;
119 method
= http_request_get_method(request
);
120 cseq
= http_request_get_header(request
, "CSeq");
121 if (!method
|| !cseq
) {
125 res
= http_response_init("RTSP/1.0", 200, "OK");
126 http_response_add_header(res
, "CSeq", cseq
);
127 http_response_add_header(res
, "Apple-Jack-Status", "connected; type=analog");
129 challenge
= http_request_get_header(request
, "Apple-Challenge");
131 char signature
[MAX_SIGNATURE_LEN
];
133 memset(signature
, 0, sizeof(signature
));
134 rsakey_sign(raop
->rsakey
, signature
, sizeof(signature
), challenge
,
135 conn
->local
, conn
->locallen
, raop
->hwaddr
, raop
->hwaddrlen
);
136 http_response_add_header(res
, "Apple-Response", signature
);
138 logger_log(&conn
->raop
->logger
, LOGGER_DEBUG
, "Got challenge: %s\n", challenge
);
139 logger_log(&conn
->raop
->logger
, LOGGER_DEBUG
, "Got response: %s\n", signature
);
141 if (!strcmp(method
, "OPTIONS")) {
142 http_response_add_header(res
, "Public", "ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER");
143 } else if (!strcmp(method
, "ANNOUNCE")) {
147 unsigned char aeskey
[16];
148 unsigned char aesiv
[16];
149 int aeskeylen
, aesivlen
;
151 data
= http_request_get_data(request
, &datalen
);
153 sdp_t
*sdp
= sdp_init(data
, datalen
);
154 logger_log(&conn
->raop
->logger
, LOGGER_DEBUG
, "rsaaeskey: %s\n", sdp_get_rsaaeskey(sdp
));
155 logger_log(&conn
->raop
->logger
, LOGGER_DEBUG
, "aesiv: %s\n", sdp_get_aesiv(sdp
));
157 aeskeylen
= rsakey_decrypt(raop
->rsakey
, aeskey
, sizeof(aeskey
),
158 sdp_get_rsaaeskey(sdp
));
159 aesivlen
= rsakey_parseiv(raop
->rsakey
, aesiv
, sizeof(aesiv
),
161 logger_log(&conn
->raop
->logger
, LOGGER_DEBUG
, "aeskeylen: %d\n", aeskeylen
);
162 logger_log(&conn
->raop
->logger
, LOGGER_DEBUG
, "aesivlen: %d\n", aesivlen
);
164 if (conn
->raop_rtp
) {
165 /* This should never happen */
166 raop_rtp_destroy(conn
->raop_rtp
);
167 conn
->raop_rtp
= NULL
;
169 conn
->raop_rtp
= raop_rtp_init(&raop
->logger
, &raop
->callbacks
, sdp_get_fmtp(sdp
), aeskey
, aesiv
);
172 } else if (!strcmp(method
, "SETUP")) {
173 unsigned short cport
=0, tport
=0, dport
=0;
174 const char *transport
;
178 transport
= http_request_get_header(request
, "Transport");
181 logger_log(&conn
->raop
->logger
, LOGGER_INFO
, "Transport: %s\n", transport
);
182 use_udp
= strncmp(transport
, "RTP/AVP/TCP", 11);
184 /* FIXME: Should use the parsed ports for resend */
185 raop_rtp_start(conn
->raop_rtp
, use_udp
, 1234, 1234, &cport
, &tport
, &dport
);
187 memset(buffer
, 0, sizeof(buffer
));
189 snprintf(buffer
, sizeof(buffer
)-1,
190 "RTP/AVP/UDP;unicast;mode=record;timing_port=%u;events;control_port=%u;server_port=%u",
191 tport
, cport
, dport
);
193 snprintf(buffer
, sizeof(buffer
)-1,
194 "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record;server_port=%u",
197 logger_log(&conn
->raop
->logger
, LOGGER_INFO
, "Responding with %s\n", buffer
);
198 http_response_add_header(res
, "Transport", buffer
);
199 http_response_add_header(res
, "Session", "DEADBEEF");
200 } else if (!strcmp(method
, "SET_PARAMETER")) {
205 data
= http_request_get_data(request
, &datalen
);
206 datastr
= calloc(1, datalen
+1);
207 if (data
&& datastr
&& conn
->raop_rtp
) {
208 memcpy(datastr
, data
, datalen
);
209 if (!strncmp(datastr
, "volume: ", 8)) {
211 sscanf(data
+8, "%f", &vol
);
212 raop_rtp_set_volume(conn
->raop_rtp
, vol
);
215 } else if (!strcmp(method
, "FLUSH")) {
219 rtpinfo
= http_request_get_header(request
, "RTP-Info");
221 logger_log(&conn
->raop
->logger
, LOGGER_INFO
, "Flush with RTP-Info: %s\n", rtpinfo
);
222 if (!strncmp(rtpinfo
, "seq=", 4)) {
223 next_seq
= strtol(rtpinfo
+4, NULL
, 10);
226 if (conn
->raop_rtp
) {
227 raop_rtp_flush(conn
->raop_rtp
, next_seq
);
229 } else if (!strcmp(method
, "TEARDOWN")) {
230 http_response_add_header(res
, "Connection", "close");
231 if (conn
->raop_rtp
) {
232 /* Destroy our RTP session */
233 raop_rtp_stop(conn
->raop_rtp
);
234 raop_rtp_destroy(conn
->raop_rtp
);
235 conn
->raop_rtp
= NULL
;
238 http_response_finish(res
, NULL
, 0);
240 logger_log(&conn
->raop
->logger
, LOGGER_DEBUG
, "Got request %s with URL %s\n", method
, http_request_get_url(request
));
245 conn_destroy(void *ptr
)
247 raop_conn_t
*conn
= ptr
;
249 if (conn
->raop_rtp
) {
250 /* This is done in case TEARDOWN was not called */
251 raop_rtp_destroy(conn
->raop_rtp
);
259 raop_init(raop_callbacks_t
*callbacks
, const char *pemkey
)
264 httpd_callbacks_t httpd_cbs
;
269 /* Initialize the network */
270 if (netutils_init() < 0) {
274 /* Validate the callbacks structure */
275 if (!callbacks
->audio_init
|| !callbacks
->audio_set_volume
||
276 !callbacks
->audio_process
|| !callbacks
->audio_flush
||
277 !callbacks
->audio_destroy
) {
281 /* Allocate the raop_t structure */
282 raop
= calloc(1, sizeof(raop_t
));
287 /* Initialize the logger */
288 logger_init(&raop
->logger
);
290 /* Set HTTP callbacks to our handlers */
291 memset(&httpd_cbs
, 0, sizeof(httpd_cbs
));
292 httpd_cbs
.opaque
= raop
;
293 httpd_cbs
.conn_init
= &conn_init
;
294 httpd_cbs
.conn_request
= &conn_request
;
295 httpd_cbs
.conn_destroy
= &conn_destroy
;
297 /* Initialize the http daemon */
298 httpd
= httpd_init(&raop
->logger
, &httpd_cbs
, 10, 1);
304 /* Copy callbacks structure */
305 memcpy(&raop
->callbacks
, callbacks
, sizeof(raop_callbacks_t
));
307 /* Initialize RSA key handler */
308 rsakey
= rsakey_init_pem(pemkey
);
316 raop
->rsakey
= rsakey
;
322 raop_init_from_keyfile(raop_callbacks_t
*callbacks
, const char *keyfile
)
327 if (utils_read_file(&pemstr
, keyfile
) < 0) {
330 raop
= raop_init(callbacks
, pemstr
);
336 raop_destroy(raop_t
*raop
)
341 httpd_destroy(raop
->httpd
);
342 rsakey_destroy(raop
->rsakey
);
345 /* Cleanup the network */
351 raop_start(raop_t
*raop
, unsigned short *port
, const char *hwaddr
, int hwaddrlen
)
357 /* Validate hardware address */
358 if (hwaddrlen
> MAX_HWADDR_LEN
) {
362 /* Copy hwaddr to the raop structure */
363 memcpy(raop
->hwaddr
, hwaddr
, hwaddrlen
);
364 raop
->hwaddrlen
= hwaddrlen
;
366 return httpd_start(raop
->httpd
, port
);
370 raop_stop(raop_t
*raop
)
374 httpd_stop(raop
->httpd
);