17 /* Actually 345 bytes for 2048-bit key */
18 #define MAX_SIGNATURE_LEN 512
21 /* Callbacks for audio */
22 raop_callbacks_t callbacks
;
27 /* HTTP daemon and RSA key */
31 /* Hardware address information */
32 unsigned char hwaddr
[MAX_HWADDR_LEN
];
43 unsigned char *remote
;
46 typedef struct raop_conn_s raop_conn_t
;
49 conn_init(void *opaque
, unsigned char *local
, int locallen
, unsigned char *remote
, int remotelen
)
54 conn
= calloc(1, sizeof(raop_conn_t
));
59 conn
->raop_rtp
= NULL
;
61 logger_log(&conn
->raop
->logger
, LOGGER_INFO
, "Local: ");
62 for (i
=0; i
<locallen
; i
++) {
63 logger_log(&conn
->raop
->logger
, LOGGER_INFO
, "%02x", local
[i
]);
65 logger_log(&conn
->raop
->logger
, LOGGER_INFO
, "\n");
66 logger_log(&conn
->raop
->logger
, LOGGER_INFO
, "Remote: ");
67 for (i
=0; i
<remotelen
; i
++) {
68 logger_log(&conn
->raop
->logger
, LOGGER_INFO
, "%02x", remote
[i
]);
70 logger_log(&conn
->raop
->logger
, LOGGER_INFO
, "\n");
72 conn
->local
= malloc(locallen
);
74 memcpy(conn
->local
, local
, locallen
);
76 conn
->remote
= malloc(remotelen
);
78 memcpy(conn
->remote
, remote
, remotelen
);
80 conn
->locallen
= locallen
;
81 conn
->remotelen
= remotelen
;
86 conn_request(void *ptr
, http_request_t
*request
, http_response_t
**response
)
88 raop_conn_t
*conn
= ptr
;
89 raop_t
*raop
= conn
->raop
;
94 const char *challenge
;
96 method
= http_request_get_method(request
);
97 cseq
= http_request_get_header(request
, "CSeq");
98 if (!method
|| !cseq
) {
102 res
= http_response_init("RTSP/1.0", 200, "OK");
103 http_response_add_header(res
, "CSeq", cseq
);
104 http_response_add_header(res
, "Apple-Jack-Status", "connected; type=analog");
106 challenge
= http_request_get_header(request
, "Apple-Challenge");
108 char signature
[MAX_SIGNATURE_LEN
];
110 memset(signature
, 0, sizeof(signature
));
111 rsakey_sign(raop
->rsakey
, signature
, sizeof(signature
), challenge
,
112 conn
->local
, conn
->locallen
, raop
->hwaddr
, raop
->hwaddrlen
);
113 logger_log(&conn
->raop
->logger
, LOGGER_DEBUG
, "Got signature: %s\n", signature
);
114 http_response_add_header(res
, "Apple-Response", signature
);
116 if (!strcmp(method
, "OPTIONS")) {
117 http_response_add_header(res
, "Public", "ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER");
118 } else if (!strcmp(method
, "ANNOUNCE")) {
122 unsigned char aeskey
[16];
123 unsigned char aesiv
[16];
124 int aeskeylen
, aesivlen
;
126 data
= http_request_get_data(request
, &datalen
);
128 sdp_t
*sdp
= sdp_init(data
, datalen
);
129 logger_log(&conn
->raop
->logger
, LOGGER_DEBUG
, "rsaaeskey: %s\n", sdp_get_rsaaeskey(sdp
));
130 logger_log(&conn
->raop
->logger
, LOGGER_DEBUG
, "aesiv: %s\n", sdp_get_aesiv(sdp
));
132 aeskeylen
= rsakey_decrypt(raop
->rsakey
, aeskey
, sizeof(aeskey
),
133 sdp_get_rsaaeskey(sdp
));
134 aesivlen
= rsakey_parseiv(raop
->rsakey
, aesiv
, sizeof(aesiv
),
136 logger_log(&conn
->raop
->logger
, LOGGER_DEBUG
, "aeskeylen: %d\n", aeskeylen
);
137 logger_log(&conn
->raop
->logger
, LOGGER_DEBUG
, "aesivlen: %d\n", aesivlen
);
139 conn
->raop_rtp
= raop_rtp_init(&raop
->logger
, &raop
->callbacks
, sdp_get_fmtp(sdp
), aeskey
, aesiv
);
142 } else if (!strcmp(method
, "SETUP")) {
143 unsigned short cport
=0, tport
=0, dport
=0;
144 const char *transport
;
148 transport
= http_request_get_header(request
, "Transport");
151 logger_log(&conn
->raop
->logger
, LOGGER_INFO
, "Transport: %s\n", transport
);
152 use_udp
= strncmp(transport
, "RTP/AVP/TCP", 11);
154 /* FIXME: Should use the parsed ports for resend */
155 raop_rtp_start(conn
->raop_rtp
, use_udp
, 1234, 1234, &cport
, &tport
, &dport
);
157 memset(buffer
, 0, sizeof(buffer
));
159 snprintf(buffer
, sizeof(buffer
)-1,
160 "RTP/AVP/UDP;unicast;mode=record;timing_port=%u;events;control_port=%u;server_port=%u",
161 tport
, cport
, dport
);
163 snprintf(buffer
, sizeof(buffer
)-1,
164 "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record;server_port=%u",
167 logger_log(&conn
->raop
->logger
, LOGGER_INFO
, "Responding with %s\n", buffer
);
168 http_response_add_header(res
, "Transport", buffer
);
169 http_response_add_header(res
, "Session", "DEADBEEF");
170 } else if (!strcmp(method
, "SET_PARAMETER")) {
175 data
= http_request_get_data(request
, &datalen
);
176 datastr
= calloc(1, datalen
+1);
178 memcpy(datastr
, data
, datalen
);
179 if (!strncmp(datastr
, "volume: ", 8)) {
181 sscanf(data
+8, "%f", &vol
);
182 raop_rtp_set_volume(conn
->raop_rtp
, vol
);
185 } else if (!strcmp(method
, "FLUSH")) {
189 rtpinfo
= http_request_get_header(request
, "RTP-Info");
192 logger_log(&conn
->raop
->logger
, LOGGER_INFO
, "RTP-Info: %s\n", rtpinfo
);
193 if (!strncmp(rtpinfo
, "seq=", 4)) {
194 next_seq
= strtol(rtpinfo
+4, NULL
, 10);
196 raop_rtp_flush(conn
->raop_rtp
, next_seq
);
197 } else if (!strcmp(method
, "TEARDOWN")) {
198 http_response_add_header(res
, "Connection", "close");
199 raop_rtp_stop(conn
->raop_rtp
);
200 raop_rtp_destroy(conn
->raop_rtp
);
201 conn
->raop_rtp
= NULL
;
203 http_response_finish(res
, NULL
, 0);
205 logger_log(&conn
->raop
->logger
, LOGGER_DEBUG
, "Got request %s with URL %s\n", method
, http_request_get_url(request
));
210 conn_destroy(void *ptr
)
212 raop_conn_t
*conn
= ptr
;
214 if (conn
->raop_rtp
) {
215 raop_rtp_destroy(conn
->raop_rtp
);
223 raop_init(raop_callbacks_t
*callbacks
, const char *pemkey
, const char *hwaddr
, int hwaddrlen
)
228 httpd_callbacks_t httpd_cbs
;
234 /* Initialize the network */
235 if (netutils_init() < 0) {
239 /* Validate the callbacks structure */
240 if (!callbacks
->audio_init
|| !callbacks
->audio_set_volume
||
241 !callbacks
->audio_process
|| !callbacks
->audio_flush
||
242 !callbacks
->audio_destroy
) {
246 /* Validate hardware address */
247 if (hwaddrlen
> MAX_HWADDR_LEN
) {
251 /* Allocate the raop_t structure */
252 raop
= calloc(1, sizeof(raop_t
));
257 /* Initialize the logger */
258 logger_init(&raop
->logger
);
260 /* Set HTTP callbacks to our handlers */
261 memset(&httpd_cbs
, 0, sizeof(httpd_cbs
));
262 httpd_cbs
.opaque
= raop
;
263 httpd_cbs
.conn_init
= &conn_init
;
264 httpd_cbs
.conn_request
= &conn_request
;
265 httpd_cbs
.conn_destroy
= &conn_destroy
;
267 /* Initialize the http daemon */
268 httpd
= httpd_init(&raop
->logger
, &httpd_cbs
, 10, 1);
274 /* Copy callbacks structure */
275 memcpy(&raop
->callbacks
, callbacks
, sizeof(raop_callbacks_t
));
277 /* Initialize RSA key handler */
278 rsakey
= rsakey_init_pem(pemkey
);
286 raop
->rsakey
= rsakey
;
288 /* Copy hwaddr to resulting structure */
289 memcpy(raop
->hwaddr
, hwaddr
, hwaddrlen
);
290 raop
->hwaddrlen
= hwaddrlen
;
296 raop_init_from_keyfile(raop_callbacks_t
*callbacks
, const char *keyfile
, const char *hwaddr
, int hwaddrlen
)
301 if (utils_read_file(&pemstr
, keyfile
) < 0) {
304 raop
= raop_init(callbacks
, pemstr
, hwaddr
, hwaddrlen
);
310 raop_destroy(raop_t
*raop
)
315 httpd_destroy(raop
->httpd
);
316 rsakey_destroy(raop
->rsakey
);
319 /* Cleanup the network */
325 raop_start(raop_t
*raop
, unsigned short *port
)
330 return httpd_start(raop
->httpd
, port
);
334 raop_stop(raop_t
*raop
)
338 httpd_stop(raop
->httpd
);