diff --git a/README.md b/README.md index 6b7b617..e6d9788 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,18 @@ Shairplay Free portable AirPlay server implementation similar to [ShairPort](https://github.com/abrasive/shairport). Currently only AirPort Express emulation is supported. +Update by foxsen, 2015/11/25: + * small fixes for AAC-ELD/ipv6 handling etc. Thanks to HaoLi for pointing them out. (untested) + * I have figured out the different handling of iso9 and will update the code when having more time. + * I am also learned most of the necessary information for the video mirroring part, but don't have enough time and interest to implement all the details and maintain them. If some guys would like to implement a fully open source shairplay that can handle both audio and video, I would like to help. Instead of the protocol implementation, I am interested more on the fairplay part and will try to get the hidden private key at spare time. I think I am close to that. + +Update by foxsen, 2015/4/20: +Experimental support for fairplay protocol and airplay: + * fairplay encrypted audio is supported (et == 3) + * AAC-ELD audio is supported + * airplay service framework is added, up to the point that the mirroring + connection starts streaming. But the UI and callbacks need to be done. + * fairplay support is performed via interactions with a server Disclaimer ---------- @@ -18,10 +30,13 @@ First you need to install some dependencies, for example on Ubuntu you would write: ``` sudo apt-get install autoconf automake libtool -sudo apt-get install libltdl-dev libao-dev libavahi-compat-libdnssd-dev +sudo apt-get install libltdl-dev libao-dev libavahi-compat-libdnssd-dev \ + libplist-dev libfdk-aac-dev libssl-dev sudo apt-get install avahi-daemon ``` +Note: The Raspbian apt repositories do not have [libplist](https://github.com/Chronic-Dev/libplist) or [libfdk-aac](https://github.com/mstorsjo/fdk-aac). You will need to compile these from source. + ``` ./autogen.sh ./configure @@ -46,6 +61,7 @@ Usage: shairplay [OPTION...] --ao_driver=driver Sets the ao driver (optional) --ao_devicename=devicename Sets the ao device name (optional) --ao_deviceid=id Sets the ao device id (optional) + --enable_airplay Start airplay service -h, --help This help ``` diff --git a/configure.ac b/configure.ac index fff796e..b6b50ed 100644 --- a/configure.ac +++ b/configure.ac @@ -79,6 +79,7 @@ AC_CONFIG_FILES( [src/Makefile] [src/lib/Makefile] [src/lib/alac/Makefile] + [src/lib/aac_eld/Makefile] [src/lib/crypto/Makefile] ) AC_OUTPUT diff --git a/include/shairplay/airplay.h b/include/shairplay/airplay.h new file mode 100644 index 0000000..29f214c --- /dev/null +++ b/include/shairplay/airplay.h @@ -0,0 +1,61 @@ +#ifndef AIRPLAY_H +#define AIRPLAY_H + +#if defined (WIN32) && defined(DLL_EXPORT) +# define AIRPLAY_API __declspec(dllexport) +#else +# define AIRPLAY_API +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Define syslog style log levels */ +#define AIRPLAY_LOG_EMERG 0 /* system is unusable */ +#define AIRPLAY_LOG_ALERT 1 /* action must be taken immediately */ +#define AIRPLAY_LOG_CRIT 2 /* critical conditions */ +#define AIRPLAY_LOG_ERR 3 /* error conditions */ +#define AIRPLAY_LOG_WARNING 4 /* warning conditions */ +#define AIRPLAY_LOG_NOTICE 5 /* normal but significant condition */ +#define AIRPLAY_LOG_INFO 6 /* informational */ +#define AIRPLAY_LOG_DEBUG 7 /* debug-level messages */ + + +typedef struct airplay_s airplay_t; + +typedef void (*airplay_log_callback_t)(void *cls, int level, const char *msg); + +struct airplay_callbacks_s { + void* cls; + + /* Compulsory callback functions */ + void* (*audio_init)(void *cls, int bits, int channels, int samplerate); + void (*audio_process)(void *cls, void *session, const void *buffer, int buflen); + void (*audio_destroy)(void *cls, void *session); + + /* Optional but recommended callback functions */ + void (*audio_flush)(void *cls, void *session); + void (*audio_set_volume)(void *cls, void *session, float volume); + void (*audio_set_metadata)(void *cls, void *session, const void *buffer, int buflen); + void (*audio_set_coverart)(void *cls, void *session, const void *buffer, int buflen); +}; +typedef struct airplay_callbacks_s airplay_callbacks_t; + +AIRPLAY_API airplay_t *airplay_init(int max_clients, airplay_callbacks_t *callbacks, const char *pemkey, int *error); +AIRPLAY_API airplay_t *airplay_init_from_keyfile(int max_clients, airplay_callbacks_t *callbacks, const char *keyfile, int *error); + +AIRPLAY_API void airplay_set_log_level(airplay_t *airplay, int level); +AIRPLAY_API void airplay_set_log_callback(airplay_t *airplay, airplay_log_callback_t callback, void *cls); + +AIRPLAY_API int airplay_start(airplay_t *airplay, unsigned short *port, const char *hwaddr, int hwaddrlen, const char *password); +AIRPLAY_API int airplay_is_running(airplay_t *airplay); +AIRPLAY_API void airplay_stop(airplay_t *airplay); + +AIRPLAY_API void airplay_destroy(airplay_t *airplay); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/Makefile.am b/src/Makefile.am index 3697bbc..f4da700 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,11 +4,12 @@ AM_CPPFLAGS = -I$(top_srcdir)/include if HAVE_LIBAO bin_PROGRAMS = shairplay - shairplay_SOURCES = shairplay.c + shairplay_SOURCES = shairplay.c audio.h audio.c shairplay_LDADD = lib/libshairplay.la shairplay_CFLAGS = shairplay_LDFLAGS = -static-libtool-libs shairplay_CFLAGS += $(libao_CFLAGS) - shairplay_LDADD += $(libao_LIBS) + shairplay_LDADD += $(libao_LIBS) endif + diff --git a/src/audio.c b/src/audio.c new file mode 100644 index 0000000..31df52c --- /dev/null +++ b/src/audio.c @@ -0,0 +1,219 @@ +/** + * Copyright (C) 2012-2013 Juho Vähä-Herttua + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#ifdef WIN32 +# include +#endif + +#include +#include "shairplay/raop.h" +//#include "lib/threads.h" + +typedef struct { + ao_device *device; + + int buffering; + int buflen; + char buffer[8192]; + + float volume; + +// int running; +// thread_handle_t thread; +} shairplay_session_t; + +static char *ao_driver = NULL, *ao_devicename = NULL, *ao_deviceid = NULL; + +static ao_device * +audio_open_device(char *driver, char *devicename, char *deviceid, int bits, int channels, int samplerate) +{ + ao_device *device = NULL; + ao_option *ao_options = NULL; + ao_sample_format format; + int driver_id; + + /* Get the libao driver ID */ + if (strlen(driver)) { + driver_id = ao_driver_id(driver); + } else { + driver_id = ao_default_driver_id(); + } + + /* Add all available libao options */ + if (strlen(devicename)) { + ao_append_option(&ao_options, "dev", devicename); + } + if (strlen(deviceid)) { + ao_append_option(&ao_options, "id", deviceid); + } + + + /* Set audio format */ + memset(&format, 0, sizeof(format)); + format.bits = bits; + format.channels = channels; + format.rate = samplerate; + format.byte_format = AO_FMT_NATIVE; + + /* Try opening the actual device */ + device = ao_open_live(driver_id, &format, ao_options); + ao_free_options(ao_options); + return device; +} + +static void * +audio_init(void *cls, int bits, int channels, int samplerate) +{ + shairplay_session_t *session; + + session = calloc(1, sizeof(shairplay_session_t)); + assert(session); + + session->device = audio_open_device(ao_driver, ao_devicename, ao_deviceid, bits, channels, samplerate); + if (session->device == NULL) { + printf("Error opening device %d\n", errno); + } + assert(session->device); + + session->buffering = 1; + session->volume = 1.0f; + + //session->running = 1; + //THREAD_CREATE(&session->thread, audio_thread, cls); + return session; +} + +static int +audio_output(shairplay_session_t *session, const void *buffer, int buflen) +{ + short *shortbuf; + char tmpbuf[4096]; + int tmpbuflen, i; + + tmpbuflen = (buflen > sizeof(tmpbuf)) ? sizeof(tmpbuf) : buflen; + memcpy(tmpbuf, buffer, tmpbuflen); + if (ao_is_big_endian()) { + for (i=0; ivolume; + } + ao_play(session->device, tmpbuf, tmpbuflen); + return tmpbuflen; +} + +static void +audio_process(void *cls, void *opaque, const void *buffer, int buflen) +{ + shairplay_session_t *session = opaque; + int processed; + + if (session->buffering) { + printf("Buffering... %d %d\n", session->buflen + buflen, sizeof(session->buffer)); + if (session->buflen+buflen < sizeof(session->buffer)) { + memcpy(session->buffer+session->buflen, buffer, buflen); + session->buflen += buflen; + return; + } + session->buffering = 0; + printf("Finished buffering...\n"); + + processed = 0; + while (processed < session->buflen) { + processed += audio_output(session, + session->buffer+processed, + session->buflen-processed); + } + session->buflen = 0; + } + + processed = 0; + while (processed < buflen) { + processed += audio_output(session, + buffer+processed, + buflen-processed); + } +} + +static void +audio_destroy(void *cls, void *opaque) +{ + shairplay_session_t *session = opaque; + + ao_close(session->device); + free(session); +} + +static void +audio_set_volume(void *cls, void *opaque, float volume) +{ + shairplay_session_t *session = opaque; + session->volume = pow(10.0, 0.05*volume); +} + +int audio_prepare(char *driver, char *devicename, char *deviceid, raop_callbacks_t *raop_cbs) +{ + ao_device *device = NULL; + + ao_initialize(); + + device = audio_open_device(driver, devicename, deviceid, 16, 2, 44100); + if (device == NULL) { + fprintf(stderr, "Error opening audio device %d\n", errno); + fprintf(stderr, "Please check your libao settings and try again\n"); + return -1; + } else { + ao_close(device); + device = NULL; + } + + ao_driver = driver; + ao_devicename = devicename; + ao_deviceid = deviceid; + + memset(raop_cbs, 0, sizeof(*raop_cbs)); + raop_cbs->cls = NULL; + raop_cbs->audio_init = audio_init; + raop_cbs->audio_process = audio_process; + raop_cbs->audio_destroy = audio_destroy; + raop_cbs->audio_set_volume = audio_set_volume; + + return 0; +} + +void audio_shutdown() +{ + ao_shutdown(); +} diff --git a/src/audio.h b/src/audio.h new file mode 100644 index 0000000..560ed94 --- /dev/null +++ b/src/audio.h @@ -0,0 +1,9 @@ +#ifndef __SHAIRPLAY_AUDIO_H__ +#define __SHAIRPLAY_AUDIO_H__ + +#include + +int audio_prepare(char *driver, char *devicename, char *deviceid, raop_callbacks_t *raop_cbs); +void audio_shutdown(); + +#endif diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 8525fc6..11f033a 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -1,13 +1,13 @@ -SUBDIRS = crypto alac +SUBDIRS = crypto alac aac_eld AM_CPPFLAGS = -I$(top_srcdir)/include/shairplay lib_LTLIBRARIES = libshairplay.la -libshairplay_la_SOURCES = base64.c base64.h digest.c digest.h dnssd.c dnssdint.h http_parser.c http_parser.h http_request.c http_request.h http_response.c http_response.h httpd.c httpd.h logger.c logger.h netutils.c netutils.h raop.c raop_buffer.c raop_buffer.h raop_rtp.c raop_rtp.h rsakey.c rsakey.h rsapem.c rsapem.h sdp.c sdp.h utils.c utils.h compat.h memalign.h sockets.h threads.h +libshairplay_la_SOURCES = base64.c base64.h digest.c digest.h dnssd.c dnssdint.h http_parser.c http_parser.h http_request.c http_request.h http_response.c http_response.h httpd.c httpd.h logger.c logger.h netutils.c netutils.h raop.c raop_buffer.c raop_buffer.h raop_rtp.c raop_rtp.h rsakey.c rsakey.h rsapem.c rsapem.h sdp.c sdp.h utils.c utils.h compat.h memalign.h sockets.h threads.h airplay.c fairplay.h fairplay.c libshairplay_la_CPPFLAGS = $(AM_CPPFLAGS) # This library depends on 3rd party libraries -libshairplay_la_LIBADD = crypto/libcrypto.la alac/libalac.la +libshairplay_la_LIBADD = crypto/libcrypto.la alac/libalac.la aac_eld/libaac_eld.la libshairplay_la_LDFLAGS = -no-undefined -version-info 0:0:0 ### Update -version-info above with the following rules @@ -26,7 +26,9 @@ libshairplay_la_LDFLAGS = -no-undefined -version-info 0:0:0 ### libshairplay_la_LIBADD += $(LIBADD_DLOPEN) -libshairplay_la_LIBADD += $(LIBM) +libshairplay_la_LIBADD += $(LIBM) +libshairplay_la_LIBADD += -lfdk-aac +libshairplay_la_LIBADD += -lplist if OS_WIN32 libshairplay_la_CPPFLAGS += -DDLL_EXPORT diff --git a/src/lib/aac_eld/Makefile.am b/src/lib/aac_eld/Makefile.am new file mode 100644 index 0000000..a57e43a --- /dev/null +++ b/src/lib/aac_eld/Makefile.am @@ -0,0 +1,2 @@ +noinst_LTLIBRARIES = libaac_eld.la +libaac_eld_la_SOURCES = aac_eld.c aac_eld.h diff --git a/src/lib/aac_eld/aac_eld.c b/src/lib/aac_eld/aac_eld.c new file mode 100644 index 0000000..04d122a --- /dev/null +++ b/src/lib/aac_eld/aac_eld.c @@ -0,0 +1,119 @@ +/* + * decode AAC-ELD audio data from mac by XBMC, and play it by SDL + * + * modify: + * 2012-10-31 first version (ffmpeg tutorial03.c) + * + */ + +#include +#include +#include +#include "aac_eld.h" + +/* ---------------------------------------------------------- */ +/* next n lines is libfdk-aac config */ +/* ---------------------------------------------------------- */ + +/* period size 480 samples */ +#define N_SAMPLE 480 +/* ASC config binary data */ +unsigned char eld_conf[] = { 0xF8, 0xE8, 0x50, 0x00 }; +unsigned char *conf[] = { eld_conf }; //TODO just for aac eld config +static unsigned int conf_len = sizeof(eld_conf); + +static int pcm_pkt_size = 4 * N_SAMPLE; + +struct aac_eld_file { + int fdk_flags; + HANDLE_AACDECODER phandle; + TRANSPORT_TYPE transportFmt; + unsigned int nrOfLayers; + CStreamInfo *stream_info; +}; + +/* + * create aac eld decoder + */ +aac_eld_file *create_aac_eld(void) +{ + int ret = 0; + aac_eld_file *aac; + + aac = malloc(sizeof(aac_eld_file)); + if (!aac) return NULL; + + aac->fdk_flags = 0; + aac->transportFmt = 0; //raw + aac->nrOfLayers = 1; // 1 layer + aac->phandle = aacDecoder_Open(aac->transportFmt, aac->nrOfLayers); + if (aac->phandle == NULL) { + printf("aacDecoder open faild!\n"); + return NULL; + } + + printf("conf_len = %d\n", conf_len); + ret = aacDecoder_ConfigRaw(aac->phandle, conf, &conf_len); + if (ret != AAC_DEC_OK) { + fprintf(stderr, "Unable to set configRaw\n"); + return NULL; + } + + aac->stream_info = aacDecoder_GetStreamInfo(aac->phandle); + if (aac->stream_info == NULL) { + printf("aacDecoder_GetStreamInfo failed!\n"); + return NULL; + } + printf("> stream info: channel = %d\tsample_rate = %d\tframe_size = %d\taot = %d\tbitrate = %d\n", \ + aac->stream_info->channelConfig, aac->stream_info->aacSampleRate, + aac->stream_info->aacSamplesPerFrame, aac->stream_info->aot, aac->stream_info->bitRate); + return aac; +} + +void destroy_aac_eld(aac_eld_file *aac) +{ + if (aac) { + aacDecoder_Close(aac->phandle); + free(aac); + } +} + +void aac_eld_set_info(aac_eld_file *aac_eld, char *inputbuffer) +{ +} + +/* + * called by external, aac data input queue + */ +void aac_eld_decode_frame(aac_eld_file *aac_eld, unsigned char *inbuffer, int inputsize, void *outbuffer, int *outputsize) +{ + int ret = 0; + unsigned char *input_buf[1]; + int sizes[1]; + int valid_size; + + input_buf[0] = inbuffer; + sizes[0] = inputsize; + + /* step 1 -> fill aac_data_buf to decoder's internal buf */ + ret = aacDecoder_Fill(aac_eld->phandle, input_buf, sizes, &valid_size); + if (ret != AAC_DEC_OK) { + fprintf(stderr, "Fill failed: %x\n", ret); + *outputsize = 0; + return; + } + + /* step 2 -> call decoder function */ + ret = aacDecoder_DecodeFrame(aac_eld->phandle, outbuffer, pcm_pkt_size, aac_eld->fdk_flags); + if (ret != AAC_DEC_OK) { + fprintf(stderr, "aacDecoder_DecodeFrame : 0x%x\n", ret); + *outputsize = 0; + return; + } + + *outputsize = pcm_pkt_size; + + /* TOCHECK: need to check and handle inputsize != valid_size ? */ + fprintf(stderr, "pcm output %d\n", *outputsize); +} + diff --git a/src/lib/aac_eld/aac_eld.h b/src/lib/aac_eld/aac_eld.h new file mode 100644 index 0000000..6fb4b8e --- /dev/null +++ b/src/lib/aac_eld/aac_eld.h @@ -0,0 +1,14 @@ +#ifndef __AAC__ELD_DECOMP_H +#define __AAC__ELD_DECOMP_H + +typedef struct aac_eld_file aac_eld_file; + +aac_eld_file *create_aac_eld(); +void aac_eld_decode_frame(aac_eld_file *aac_eld, + unsigned char *inbuffer, int inputsize, + void *outbuffer, int *outputsize); +void aac_eld_set_info(aac_eld_file *aac_eld, char *inputbuffer); +void destroy_aac_eld(aac_eld_file *aac_eld); + +#endif /* __ALAC__DECOMP_H */ + diff --git a/src/lib/airplay.c b/src/lib/airplay.c new file mode 100644 index 0000000..5650ad9 --- /dev/null +++ b/src/lib/airplay.c @@ -0,0 +1,1035 @@ +/** + * Copyright (C) 2011-2012 Juho Vähä-Herttua + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#include "airplay.h" +#include "raop_rtp.h" +#include "rsakey.h" +#include "digest.h" +#include "httpd.h" +#include "sdp.h" + +#include "global.h" +#include "utils.h" +#include "netutils.h" +#include "logger.h" +#include "compat.h" +#include "fairplay.h" + +/* Actually 345 bytes for 2048-bit key */ +#define MAX_SIGNATURE_LEN 512 + +/* Let's just decide on some length */ +#define MAX_PASSWORD_LEN 64 + +/* MD5 as hex fits here */ +#define MAX_NONCE_LEN 32 + +#define MAX_PACKET_LEN 4096 + +struct airplay_s { + /* Callbacks for audio */ + airplay_callbacks_t callbacks; + + /* Logger instance */ + logger_t *logger; + + /* HTTP daemon and RSA key */ + httpd_t *httpd; + rsakey_t *rsakey; + + httpd_t *mirror_server; + + /* Hardware address information */ + unsigned char hwaddr[MAX_HWADDR_LEN]; + int hwaddrlen; + + /* Password information */ + char password[MAX_PASSWORD_LEN+1]; +}; + +struct airplay_conn_s { + airplay_t *airplay; + raop_rtp_t *airplay_rtp; + + unsigned char *local; + int locallen; + + unsigned char *remote; + int remotelen; + + char nonce[MAX_NONCE_LEN+1]; + + /* for mirror stream */ + unsigned char aeskey[16]; + unsigned char iv[16]; + unsigned char buffer[MAX_PACKET_LEN]; + int pos; +}; +typedef struct airplay_conn_s airplay_conn_t; + +#define RECEIVEBUFFER 1024 + +#define AIRPLAY_STATUS_OK 200 +#define AIRPLAY_STATUS_SWITCHING_PROTOCOLS 101 +#define AIRPLAY_STATUS_NEED_AUTH 401 +#define AIRPLAY_STATUS_NOT_FOUND 404 +#define AIRPLAY_STATUS_METHOD_NOT_ALLOWED 405 +#define AIRPLAY_STATUS_PRECONDITION_FAILED 412 +#define AIRPLAY_STATUS_NOT_IMPLEMENTED 501 +#define AIRPLAY_STATUS_NO_RESPONSE_NEEDED 1000 + +#define EVENT_NONE -1 +#define EVENT_PLAYING 0 +#define EVENT_PAUSED 1 +#define EVENT_LOADING 2 +#define EVENT_STOPPED 3 +const char *eventStrings[] = {"playing", "paused", "loading", "stopped"}; + +#define STREAM_INFO "\r\n"\ +"\r\n"\ +"\r\n"\ +"\r\n"\ +"width\r\n"\ +"1280\r\n"\ +"height\r\n"\ +"720\r\n"\ +"version\r\n"\ +"110.92\r\n"\ +"\r\n"\ +"\r\n" + +#define PLAYBACK_INFO "\r\n"\ +"\r\n"\ +"\r\n"\ +"\r\n"\ +"duration\r\n"\ +"%f\r\n"\ +"loadedTimeRanges\r\n"\ +"\r\n"\ +"\t\t\r\n"\ +"\t\t\tduration\r\n"\ +"\t\t\t%f\r\n"\ +"\t\t\tstart\r\n"\ +"\t\t\t0.0\r\n"\ +"\t\t\r\n"\ +"\r\n"\ +"playbackBufferEmpty\r\n"\ +"\r\n"\ +"playbackBufferFull\r\n"\ +"\r\n"\ +"playbackLikelyToKeepUp\r\n"\ +"\r\n"\ +"position\r\n"\ +"%f\r\n"\ +"rate\r\n"\ +"%d\r\n"\ +"readyToPlay\r\n"\ +"\r\n"\ +"seekableTimeRanges\r\n"\ +"\r\n"\ +"\t\t\r\n"\ +"\t\t\tduration\r\n"\ +"\t\t\t%f\r\n"\ +"\t\t\tstart\r\n"\ +"\t\t\t0.0\r\n"\ +"\t\t\r\n"\ +"\r\n"\ +"\r\n"\ +"\r\n" + +#define PLAYBACK_INFO_NOT_READY "\r\n"\ +"\r\n"\ +"\r\n"\ +"\r\n"\ +"readyToPlay\r\n"\ +"\r\n"\ +"\r\n"\ +"\r\n" + +#define SERVER_INFO "\r\n"\ +"\r\n"\ +"\r\n"\ +"\r\n"\ +"deviceid\r\n"\ +"%s\r\n"\ +"features\r\n"\ +"119\r\n"\ +"model\r\n"\ +"Kodi,1\r\n"\ +"protovers\r\n"\ +"1.0\r\n"\ +"srcvers\r\n"\ +""AIRPLAY_SERVER_VERSION_STR"\r\n"\ +"\r\n"\ +"\r\n" + +#define EVENT_INFO "\n\r\n"\ +"\n\r\n"\ +"\r\n"\ +"\r\n"\ +"category\r\n"\ +"video\r\n"\ +"sessionID\r\n"\ +"%d\r\n"\ +"state\r\n"\ +"%s\r\n"\ +"\r\n"\ +"\r\n"\ + +#define AUTH_REALM "AirPlay" +#define AUTH_REQUIRED "WWW-Authenticate: Digest realm=\"" AUTH_REALM "\", nonce=\"%s\"\r\n" + +static void * +conn_init(void *opaque, unsigned char *local, int locallen, unsigned char *remote, int remotelen) +{ + airplay_conn_t *conn; + + conn = calloc(1, sizeof(airplay_conn_t)); + if (!conn) { + return NULL; + } + conn->airplay = opaque; + conn->airplay_rtp = NULL; + + if (locallen == 4) { + logger_log(conn->airplay->logger, LOGGER_INFO, + "Local: %d.%d.%d.%d", + local[0], local[1], local[2], local[3]); + } else if (locallen == 16) { + logger_log(conn->airplay->logger, LOGGER_INFO, + "Local: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", + local[0], local[1], local[2], local[3], local[4], local[5], local[6], local[7], + local[8], local[9], local[10], local[11], local[12], local[13], local[14], local[15]); + } + if (remotelen == 4) { + logger_log(conn->airplay->logger, LOGGER_INFO, + "Remote: %d.%d.%d.%d", + remote[0], remote[1], remote[2], remote[3]); + } else if (remotelen == 16) { + logger_log(conn->airplay->logger, LOGGER_INFO, + "Remote: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", + remote[0], remote[1], remote[2], remote[3], remote[4], remote[5], remote[6], remote[7], + remote[8], remote[9], remote[10], remote[11], remote[12], remote[13], remote[14], remote[15]); + } + + conn->local = malloc(locallen); + assert(conn->local); + memcpy(conn->local, local, locallen); + + conn->remote = malloc(remotelen); + assert(conn->remote); + memcpy(conn->remote, remote, remotelen); + + conn->locallen = locallen; + conn->remotelen = remotelen; + + digest_generate_nonce(conn->nonce, sizeof(conn->nonce)); + return conn; +} + +static void +conn_request(void *ptr, http_request_t *request, http_response_t **response) +{ + const char realm[] = "airplay"; + airplay_conn_t *conn = ptr; + airplay_t *airplay = conn->airplay; + + const char *method; + const char *cseq; + const char *challenge; + int require_auth = 0; + char responseHeader[4096]; + char responseBody[4096]; + int responseLength = 0; + + const char * uri = http_request_get_url(request); + method = http_request_get_method(request); + + if (!method) { + return; + } + + const char * contentType = http_request_get_header(request, "content-type"); + const char * m_sessionId = http_request_get_header(request, "x-apple-session-id"); + const char * authorization = http_request_get_header(request, "authorization"); + const char * photoAction = http_request_get_header(request, "x-apple-assetaction"); + const char * photoCacheId = http_request_get_header(request, "x-apple-assetkey"); + + int status = AIRPLAY_STATUS_OK; + int needAuth = 0; + + logger_log(conn->airplay->logger, LOGGER_DEBUG,"%s uri=%s\n", method, uri); + + { + const char *data; + int len; + data = http_request_get_data(request,&len); + logger_log(conn->airplay->logger, LOGGER_DEBUG,"data len %d:%s\n", len, data); + } + + /* TODO: the following code borrowed from xbmc, to fix */ + + /* + size_t startQs = uri.find('?'); + if (startQs != char *::npos) + { + uri.erase(startQs); + } + */ + + // This is the socket which will be used for reverse HTTP + // negotiate reverse HTTP via upgrade + if (strcmp(uri, "/reverse") == 0) + { + status = AIRPLAY_STATUS_SWITCHING_PROTOCOLS; + sprintf(responseHeader, "Upgrade: PTTH/1.0\r\nConnection: Upgrade\r\n"); + } + + // The rate command is used to play/pause media. + // A value argument should be supplied which indicates media should be played or paused. + // 0.000000 => pause + // 1.000000 => play + else if (strcmp(uri, "/rate") == 0) + { +/* + const char* found = strstr(queryString.c_str(), "value="); + int rate = found ? (int)(atof(found + strlen("value=")) + 0.5f) : 0; + + logger_log(conn->airplay->logger, LOGGER_INFO, "AIRPLAY: got request %s with rate %i", uri.c_str(), rate); + + if (needAuth && !checkAuthorization(authorization, method, uri)) + { + status = AIRPLAY_STATUS_NEED_AUTH; + } + else if (rate == 0) + { + if (g_application.m_pPlayer->IsPlaying() && !g_application.m_pPlayer->IsPaused()) + { + CApplicationMessenger::Get().MediaPause(); + } + } + else + { + if (g_application.m_pPlayer->IsPausedPlayback()) + { + CApplicationMessenger::Get().MediaPause(); + } + } +*/ + } + + // The volume command is used to change playback volume. + // A value argument should be supplied which indicates how loud we should get. + // 0.000000 => silent + // 1.000000 => loud + else if (strcmp(uri, "/volume") == 0) + { +/* + const char* found = strstr(queryString.c_str(), "volume="); + float volume = found ? (float)strtod(found + strlen("volume="), NULL) : 0; + + logger_log(conn->airplay->logger, LOGGER_INFO, "AIRPLAY: got request %s with volume %f", uri.c_str(), volume); + + if (needAuth && !checkAuthorization(authorization, method, uri)) + { + status = AIRPLAY_STATUS_NEED_AUTH; + } + else if (volume >= 0 && volume <= 1) + { + float oldVolume = g_application.GetVolume(); + volume *= 100; + if(oldVolume != volume && CSettings::Get().GetBool("services.airplayvolumecontrol")) + { + backupVolume(); + g_application.SetVolume(volume); + CApplicationMessenger::Get().ShowVolumeBar(oldVolume < volume); + } + } +*/ + } + + + // Contains a header like format in the request body which should contain a + // Content-Location and optionally a Start-Position + else if (strcmp(uri, "/play") == 0) + { +/* + char * location; + float position = 0.0; + int startPlayback = true; + m_lastEvent = EVENT_NONE; + + logger_log(conn->airplay->logger, LOGGER_INFO, "AIRPLAY: got request %s", uri.c_str()); + + if (needAuth && !checkAuthorization(authorization, method, uri)) + { + status = AIRPLAY_STATUS_NEED_AUTH; + } + else if (contentType == "application/x-apple-binary-plist") + { + m_isPlaying++; + + if (m_pLibPlist->Load()) + { + m_pLibPlist->EnableDelayedUnload(false); + + const char* bodyChr = m_httpParser->getBody(); + + plist_t dict = NULL; + m_pLibPlist->plist_from_bin(bodyChr, m_httpParser->getContentLength(), &dict); + + if (m_pLibPlist->plist_dict_get_size(dict)) + { + plist_t tmpNode = m_pLibPlist->plist_dict_get_item(dict, "Start-Position"); + if (tmpNode) + { + double tmpDouble = 0; + m_pLibPlist->plist_get_real_val(tmpNode, &tmpDouble); + position = (float)tmpDouble; + } + + tmpNode = m_pLibPlist->plist_dict_get_item(dict, "Content-Location"); + if (tmpNode) + { + location = getStringFromPlist(m_pLibPlist, tmpNode); + tmpNode = NULL; + } + + tmpNode = m_pLibPlist->plist_dict_get_item(dict, "rate"); + if (tmpNode) + { + double rate = 0; + m_pLibPlist->plist_get_real_val(tmpNode, &rate); + if (rate == 0.0) + { + startPlayback = false; + } + tmpNode = NULL; + } + + // in newer protocol versions the location is given + // via host and path where host is ip:port and path is /path/file.mov + if (location.empty()) + tmpNode = m_pLibPlist->plist_dict_get_item(dict, "host"); + if (tmpNode) + { + location = "http://"; + location += getStringFromPlist(m_pLibPlist, tmpNode); + + tmpNode = m_pLibPlist->plist_dict_get_item(dict, "path"); + if (tmpNode) + { + location += getStringFromPlist(m_pLibPlist, tmpNode); + } + } + + if (dict) + { + m_pLibPlist->plist_free(dict); + } + } + else + { + logger_log(conn->airplay->logger, LOGGER_INFO, "Error parsing plist"); + } + m_pLibPlist->Unload(); + } + } + else + { + m_isPlaying++; + // Get URL to play + char * contentLocation = "Content-Location: "; + size_t start = body.find(contentLocation); + if (start == char *::npos) + return AIRPLAY_STATUS_NOT_IMPLEMENTED; + start += contentLocation.size(); + int end = body.find('\n', start); + location = body.substr(start, end - start); + + char * startPosition = "Start-Position: "; + start = body.find(startPosition); + if (start != char *::npos) + { + start += startPosition.size(); + int end = body.find('\n', start); + char * positionStr = body.substr(start, end - start); + position = (float)atof(positionStr.c_str()); + } + } + + if (status != AIRPLAY_STATUS_NEED_AUTH) + { + char * userAgent(CURL::Encode("AppleCoreMedia/1.0.0.8F455 (AppleTV; U; CPU OS 4_3 like Mac OS X; de_de)")); + location += "|User-Agent=" + userAgent; + + CFileItem fileToPlay(location, false); + fileToPlay.SetProperty("StartPercent", position*100.0f); + airplayp->AnnounceToClients(EVENT_LOADING); + // froce to internal dvdplayer cause it is the only + // one who will work well with airplay + g_application.m_eForcedNextPlayer = EPC_DVDPLAYER; + CApplicationMessenger::Get().MediaPlay(fileToPlay); + + // allow starting the player paused in ios8 mode (needed by camera roll app) + if (CSettings::Get().GetBool("services.airplayios8compat") && !startPlayback) + { + CApplicationMessenger::Get().MediaPause(); + g_application.m_pPlayer->SeekPercentage(position * 100.0f); + } + } +*/ + } + + // Used to perform seeking (POST request) and to retrieve current player position (GET request). + // GET scrub seems to also set rate 1 - strange but true + else if (strcmp(uri, "/scrub") == 0) + { +/* + if (needAuth && !checkAuthorization(authorization, method, uri)) + { + status = AIRPLAY_STATUS_NEED_AUTH; + } + else if (method == "GET") + { + logger_log(conn->airplay->logger, LOGGER_INFO, "AIRPLAY: got GET request %s", uri.c_str()); + + if (g_application.m_pPlayer->GetTotalTime()) + { + float position = ((float) g_application.m_pPlayer->GetTime()) / 1000; + sprintf(responseBody, "duration: %.6f\r\nposition: %.6f\r\n", (float)g_application.m_pPlayer->GetTotalTime() / 1000, position); + } + else + { + status = AIRPLAY_STATUS_METHOD_NOT_ALLOWED; + } + } + else + { + const char* found = strstr(queryString.c_str(), "position="); + + if (found && g_application.m_pPlayer->HasPlayer()) + { + int64_t position = (int64_t) (atof(found + strlen("position=")) * 1000.0); + g_application.m_pPlayer->SeekTime(position); + logger_log(conn->airplay->logger, LOGGER_INFO, "AIRPLAY: got POST request %s with pos %" PRId64, uri.c_str(), position); + } + } +*/ + } + + // Sent when media playback should be stopped + else if (strcmp(uri, "/stop") == 0) + { +/* + logger_log(conn->airplay->logger, LOGGER_INFO, "AIRPLAY: got request %s", uri.c_str()); + if (needAuth && !checkAuthorization(authorization, method, uri)) + { + status = AIRPLAY_STATUS_NEED_AUTH; + } + else + { + if (IsPlaying()) //only stop player if we started him + { + CApplicationMessenger::Get().MediaStop(); + m_isPlaying--; + } + else //if we are not playing and get the stop request - we just wanna stop picture streaming + { + CApplicationMessenger::Get().SendAction(ACTION_PREVIOUS_MENU); + } + } + ClearPhotoAssetCache(); +*/ + } + + // RAW JPEG data is contained in the request body + else if (strcmp(uri, "/photo") == 0) + { +/* + logger_log(conn->airplay->logger, LOGGER_INFO, "AIRPLAY: got request %s", uri.c_str()); + if (needAuth && !checkAuthorization(authorization, method, uri)) + { + status = AIRPLAY_STATUS_NEED_AUTH; + } + else if (m_httpParser->getContentLength() > 0 || photoAction == "displayCached") + { + XFILE::CFile tmpFile; + char * tmpFileName = "special://temp/airplayasset"; + int showPhoto = true; + int receivePhoto = true; + + + if (photoAction == "cacheOnly") + showPhoto = false; + else if (photoAction == "displayCached") + { + receivePhoto = false; + if (photoCacheId.length()) + logger_log(conn->airplay->logger, LOGGER_INFO, "AIRPLAY: Trying to show from cache asset: %s", photoCacheId.c_str()); + } + + if (photoCacheId.length()) + tmpFileName += photoCacheId; + else + tmpFileName += "airplay_photo"; + + if( receivePhoto && m_httpParser->getContentLength() > 3 && + m_httpParser->getBody()[1] == 'P' && + m_httpParser->getBody()[2] == 'N' && + m_httpParser->getBody()[3] == 'G') + { + tmpFileName += ".png"; + } + else + { + tmpFileName += ".jpg"; + } + + int writtenBytes=0; + if (receivePhoto) + { + if (tmpFile.OpenForWrite(tmpFileName, true)) + { + writtenBytes = tmpFile.Write(m_httpParser->getBody(), m_httpParser->getContentLength()); + tmpFile.Close(); + } + if (photoCacheId.length()) + logger_log(conn->airplay->logger, LOGGER_INFO, "AIRPLAY: Cached asset: %s", photoCacheId.c_str()); + } + + if (showPhoto) + { + if ((writtenBytes > 0 && (unsigned int)writtenBytes == m_httpParser->getContentLength()) || !receivePhoto) + { + if (!receivePhoto && !XFILE::CFile::Exists(tmpFileName)) + { + status = AIRPLAY_STATUS_PRECONDITION_FAILED; //image not found in the cache + if (photoCacheId.length()) + CLog::Log(LOGWARNING, "AIRPLAY: Asset %s not found in our cache.", photoCacheId.c_str()); + } + else + CApplicationMessenger::Get().PictureShow(tmpFileName); + } + else + { + logger_log(conn->airplay->logger, LOGGER_INFO,"AirPlayServer: Error writing tmpFile."); + } + } + } +*/ + } + + else if (strcmp(uri, "/playback-info") == 0) + { +/* + float position = 0.0f; + float duration = 0.0f; + float cachePosition = 0.0f; + int playing = false; + + logger_log(conn->airplay->logger, LOGGER_INFO, "AIRPLAY: got request %s", uri.c_str()); + + if (needAuth && !checkAuthorization(authorization, method, uri)) + { + status = AIRPLAY_STATUS_NEED_AUTH; + } + else if (g_application.m_pPlayer->HasPlayer()) + { + if (g_application.m_pPlayer->GetTotalTime()) + { + position = ((float) g_application.m_pPlayer->GetTime()) / 1000; + duration = ((float) g_application.m_pPlayer->GetTotalTime()) / 1000; + playing = !g_application.m_pPlayer->IsPaused(); + cachePosition = position + (duration * g_application.m_pPlayer->GetCachePercentage() / 100.0f); + } + + sprintf(responseBody, PLAYBACK_INFO, duration, cachePosition, position, (playing ? 1 : 0), duration); + sprintf(responseHeader, "Content-Type: text/x-apple-plist+xml\r\n"); + + if (g_application.m_pPlayer->IsCaching()) + { + airplayp->AnnounceToClients(EVENT_LOADING); + } + } + else + { + sprintf(responseBody, PLAYBACK_INFO_NOT_READY); + sprintf(responseHeader, "Content-Type: text/x-apple-plist+xml\r\n"); + } +*/ + } + + else if (strcmp(uri, "/stream.xml") == 0) + { + logger_log(conn->airplay->logger, LOGGER_INFO, "AIRPLAY: got request %s", uri); + sprintf(responseBody, "%s", STREAM_INFO); + sprintf(responseHeader, "Content-Type: text/x-apple-plist+xml\r\n"); + + } + else if (strcmp(uri, "/stream") == 0) + { + const char *plist_bin = NULL; + char *xml = NULL; + int size = 0; + int type; + plist_t root = NULL; + + plist_bin = http_request_get_data(request, &size); + plist_from_bin(plist_bin, size, &root); + if (root) { + plist_to_xml(root, &xml, &size); + /* TODO: in this plist, we will get param1¶m2, which is the + encoded aeskey & aesiv */ + if (xml) fprintf(stderr, "%s\n", xml); + /* after /stream, this connection will no longer a http session */ + httpd_set_mirror_streaming(conn->airplay->mirror_server); + return; + } else { + logger_log(conn->airplay->logger, LOGGER_INFO, "AIRPLAY: Invalid bplist"); + status = AIRPLAY_STATUS_NOT_FOUND; + } + } + else if (strcmp(uri, "/server-info") == 0) + { + logger_log(conn->airplay->logger, LOGGER_INFO, "AIRPLAY: got request %s", uri); + sprintf(responseBody, "%s\r\n", conn->airplay->hwaddr); + sprintf(responseHeader, "Content-Type: text/x-apple-plist+xml\r\n"); + } + + else if (strcmp(uri, "/slideshow-features") == 0) + { + // Ignore for now. + } + + else if (strcmp(uri, "/authorize") == 0) + { + // DRM, ignore for now. + } + + else if (strcmp(uri, "/setProperty") == 0) + { + status = AIRPLAY_STATUS_NOT_FOUND; + } + + else if (strcmp(uri, "/getProperty") == 0) + { + status = AIRPLAY_STATUS_NOT_FOUND; + } + + else if (strcmp(uri, "/fp-setup") == 0) + { + //status = AIRPLAY_STATUS_PRECONDITION_FAILED; + const unsigned char *data; + int datalen, size; + char *buf; + + data = http_request_get_data(request, &datalen); + + buf = fairplay_query((datalen==16?1:2), data, datalen, &size); + + if (buf) { + memcpy(responseBody, buf, size); + responseLength = size; + sprintf(responseHeader, "Content-Type: application/octet-stream\r\n"); + } + } + + else if (strcmp(uri, "200") == 0) //response OK from the event reverse message + { + status = AIRPLAY_STATUS_NO_RESPONSE_NEEDED; + } + else + { + logger_log(conn->airplay->logger, LOGGER_INFO, "AIRPLAY Server: unhandled request [%s]\n", uri); + status = AIRPLAY_STATUS_NOT_IMPLEMENTED; + } + + if (status == AIRPLAY_STATUS_NEED_AUTH) + { + //ComposeAuthRequestAnswer(responseHeader, responseBody); + } + + char * statusMsg = "OK"; + + switch(status) + { + case AIRPLAY_STATUS_NOT_IMPLEMENTED: + statusMsg = "Not Implemented"; + break; + case AIRPLAY_STATUS_SWITCHING_PROTOCOLS: + statusMsg = "Switching Protocols"; + //reverseSockets[sessionId] = m_socket;//save this socket as reverse http socket for this sessionid + break; + case AIRPLAY_STATUS_NEED_AUTH: + statusMsg = "Unauthorized"; + break; + case AIRPLAY_STATUS_NOT_FOUND: + statusMsg = "Not Found"; + break; + case AIRPLAY_STATUS_METHOD_NOT_ALLOWED: + statusMsg = "Method Not Allowed"; + break; + case AIRPLAY_STATUS_PRECONDITION_FAILED: + statusMsg = "Precondition Failed"; + break; + } + + // Prepare the response + char resbuf[4096]; + const time_t ltime = time(NULL); + char *date = asctime(gmtime(<ime)); //Fri, 17 Dec 2010 11:18:01 GMT; + int headerLength = 0; + date[strlen(date) - 1] = '\0'; // remove \n + sprintf(resbuf, "HTTP/1.1 %d %s\nDate: %s\r\n", status, statusMsg, date); + if (responseHeader[0] != '\0') + { + strcat(resbuf, responseHeader); + } + + if (responseLength == 0) responseLength = strlen(responseBody); + + sprintf(resbuf, "%sContent-Length: %d\r\n\r\n", resbuf, responseLength); + + headerLength = strlen(resbuf); + + if (responseLength) + { + memcpy(resbuf + strlen(resbuf), responseBody, responseLength); + resbuf[headerLength + responseLength] = 0; + } + + http_response_t *res; + res = http_response_init1(resbuf, headerLength + responseLength); + + logger_log(conn->airplay->logger, LOGGER_DEBUG, "AIRPLAY Handled request %s with response %s", method, http_response_get_data(res,&responseLength)); + *response = res; +} + +static void +conn_destroy(void *ptr) +{ + airplay_conn_t *conn = ptr; + + if (conn->airplay_rtp) { + /* This is done in case TEARDOWN was not called */ + raop_rtp_destroy(conn->airplay_rtp); + } + free(conn->local); + free(conn->remote); + free(conn); +} + +static void +conn_datafeed(void *ptr, unsigned char *data, int len) +{ + int size; + unsigned short type; + unsigned short type1; + + airplay_conn_t *conn = ptr; + + size = *(int*)data; + type = *(unsigned short*)(data + 4); + type1 = *(unsigned short*)(data + 6); + + logger_log(conn->airplay->logger, LOGGER_DEBUG, "Add data size=%d type %2x %2x", size, type, type1); +} + +airplay_t * +airplay_init(int max_clients, airplay_callbacks_t *callbacks, const char *pemkey, int *error) +{ + airplay_t *airplay; + httpd_t *httpd; + httpd_t *mirror_server; + rsakey_t *rsakey; + httpd_callbacks_t httpd_cbs; + + assert(callbacks); + assert(max_clients > 0); + assert(max_clients < 100); + assert(pemkey); + + /* Initialize the network */ + if (netutils_init() < 0) { + return NULL; + } + + /* Validate the callbacks structure */ + if (!callbacks->audio_init || + !callbacks->audio_process || + !callbacks->audio_destroy) { + return NULL; + } + + /* Allocate the airplay_t structure */ + airplay = calloc(1, sizeof(airplay_t)); + if (!airplay) { + return NULL; + } + + /* Initialize the logger */ + airplay->logger = logger_init(); + + /* Set HTTP callbacks to our handlers */ + memset(&httpd_cbs, 0, sizeof(httpd_cbs)); + httpd_cbs.opaque = airplay; + httpd_cbs.conn_init = &conn_init; + httpd_cbs.conn_request = &conn_request; + httpd_cbs.conn_destroy = &conn_destroy; + httpd_cbs.conn_datafeed = &conn_datafeed; + + /* Initialize the http daemon */ + httpd = httpd_init(airplay->logger, &httpd_cbs, max_clients); + if (!httpd) { + free(airplay); + return NULL; + } + + /* Initialize the mirror server daemon */ + mirror_server = httpd_init(airplay->logger, &httpd_cbs, max_clients); + if (!mirror_server) { + free(httpd); + free(airplay); + return NULL; + } + + /* Copy callbacks structure */ + memcpy(&airplay->callbacks, callbacks, sizeof(airplay_callbacks_t)); + + /* Initialize RSA key handler */ + rsakey = rsakey_init_pem(pemkey); + if (!rsakey) { + free(httpd); + free(mirror_server); + free(airplay); + return NULL; + } + + airplay->httpd = httpd; + airplay->rsakey = rsakey; + + airplay->mirror_server = mirror_server; + + return airplay; +} + +airplay_t * +airplay_init_from_keyfile(int max_clients, airplay_callbacks_t *callbacks, const char *keyfile, int *error) +{ + airplay_t *airplay; + char *pemstr; + + if (utils_read_file(&pemstr, keyfile) < 0) { + return NULL; + } + airplay = airplay_init(max_clients, callbacks, pemstr, error); + free(pemstr); + return airplay; +} + +void +airplay_destroy(airplay_t *airplay) +{ + if (airplay) { + airplay_stop(airplay); + + httpd_destroy(airplay->httpd); + httpd_destroy(airplay->mirror_server); + rsakey_destroy(airplay->rsakey); + logger_destroy(airplay->logger); + free(airplay); + + /* Cleanup the network */ + netutils_cleanup(); + } +} + +int +airplay_is_running(airplay_t *airplay) +{ + assert(airplay); + + return httpd_is_running(airplay->httpd); +} + +void +airplay_set_log_level(airplay_t *airplay, int level) +{ + assert(airplay); + + logger_set_level(airplay->logger, level); +} + +void +airplay_set_log_callback(airplay_t *airplay, airplay_log_callback_t callback, void *cls) +{ + assert(airplay); + + logger_set_callback(airplay->logger, callback, cls); +} + +int +airplay_start(airplay_t *airplay, unsigned short *port, const char *hwaddr, int hwaddrlen, const char *password) +{ + int ret; + unsigned short mirror_port; + + assert(airplay); + assert(port); + assert(hwaddr); + + /* Validate hardware address */ + if (hwaddrlen > MAX_HWADDR_LEN) { + return -1; + } + + memset(airplay->password, 0, sizeof(airplay->password)); + if (password) { + /* Validate password */ + if (strlen(password) > MAX_PASSWORD_LEN) { + return -1; + } + + /* Copy password to the airplay structure */ + strncpy(airplay->password, password, MAX_PASSWORD_LEN); + } + + /* Copy hwaddr to the airplay structure */ + memcpy(airplay->hwaddr, hwaddr, hwaddrlen); + airplay->hwaddrlen = hwaddrlen; + + ret = httpd_start(airplay->httpd, port); + if (ret != 1) return ret; + + mirror_port = 7100; + ret = httpd_start(airplay->mirror_server, &mirror_port); + return ret; +} + +void +airplay_stop(airplay_t *airplay) +{ + assert(airplay); + + httpd_stop(airplay->httpd); + httpd_stop(airplay->mirror_server); +} + diff --git a/src/lib/alac/alac.c b/src/lib/alac/alac.c index 195e66a..e073554 100644 --- a/src/lib/alac/alac.c +++ b/src/lib/alac/alac.c @@ -760,7 +760,7 @@ void deinterlace_24(int32_t *buffer_a, int32_t *buffer_b, } -void decode_frame(alac_file *alac, +void alac_decode_frame(alac_file *alac, unsigned char *inbuffer, void *outbuffer, int *outputsize) { @@ -773,6 +773,11 @@ void decode_frame(alac_file *alac, channels = readbits(alac, 3); + if (channels != 1) { + fprintf(stderr, "wrong channels %d\n", channels); + return; + } + *outputsize = outputsamples * alac->bytespersample; switch(channels) @@ -981,6 +986,10 @@ void decode_frame(alac_file *alac, /* now read the number of samples, * as a 32bit integer */ outputsamples = readbits(alac, 32); + if (outputsamples < 0 || outputsamples > alac->setinfo_max_samples_per_frame) { + fprintf(stderr, "wrong outputsamples %d\n", outputsamples); + return; + } *outputsize = outputsamples * alac->bytespersample; } @@ -1031,6 +1040,11 @@ void decode_frame(alac_file *alac, predictor_coef_table_b[i] = (int16_t)readbits(alac, 16); } + if (prediction_type_a != 0 || prediction_type_b != 0) { + fprintf(stderr, "prediction type %d %d!\n", prediction_type_a, prediction_type_b); + return; + } + /*********************/ if (uncompressed_bytes) { /* see mono case */ diff --git a/src/lib/alac/alac.h b/src/lib/alac/alac.h index e5bf814..cce7a61 100644 --- a/src/lib/alac/alac.h +++ b/src/lib/alac/alac.h @@ -4,7 +4,7 @@ typedef struct alac_file alac_file; alac_file *create_alac(int samplesize, int numchannels); -void decode_frame(alac_file *alac, +void alac_decode_frame(alac_file *alac, unsigned char *inbuffer, void *outbuffer, int *outputsize); void alac_set_info(alac_file *alac, char *inputbuffer); diff --git a/src/lib/crypto/Makefile.am b/src/lib/crypto/Makefile.am index 26bd535..e3f8440 100644 --- a/src/lib/crypto/Makefile.am +++ b/src/lib/crypto/Makefile.am @@ -1,3 +1,3 @@ noinst_LTLIBRARIES = libcrypto.la -libcrypto_la_SOURCES = bigint.c bigint.h bigint_impl.h aes.c hmac.c md5.c rc4.c sha1.c crypto.h os_port.h config.h +libcrypto_la_SOURCES = bigint.c bigint.h bigint_impl.h aes.c hmac.c md5.c rc4.c sha1.c crypto.h os_port.h config.h new_aes.c aes_locl.h diff --git a/src/lib/crypto/aes_locl.h b/src/lib/crypto/aes_locl.h new file mode 100644 index 0000000..054b442 --- /dev/null +++ b/src/lib/crypto/aes_locl.h @@ -0,0 +1,89 @@ +/* crypto/aes/aes.h -*- mode:C; c-file-style: "eay" -*- */ +/* ==================================================================== + * Copyright (c) 1998-2002 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + */ + +#ifndef HEADER_AES_LOCL_H +#define HEADER_AES_LOCL_H + +#include + +#ifdef OPENSSL_NO_AES +#error AES is disabled. +#endif + +#include +#include +#include + +#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_X64)) +# define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00) +# define GETU32(p) SWAP(*((u32 *)(p))) +# define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); } +#else +# define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3])) +# define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } +#endif + +#ifdef AES_LONG +typedef unsigned long u32; +#else +typedef unsigned int u32; +#endif +typedef unsigned short u16; +typedef unsigned char u8; + +#define MAXKC (256/32) +#define MAXKB (256/8) +#define MAXNR 14 + +/* This controls loop-unrolling in aes_core.c */ +#undef FULL_UNROLL + +#endif /* !HEADER_AES_LOCL_H */ diff --git a/src/lib/crypto/crypto.h b/src/lib/crypto/crypto.h index a1c6ada..e814415 100644 --- a/src/lib/crypto/crypto.h +++ b/src/lib/crypto/crypto.h @@ -40,6 +40,7 @@ extern "C" { #endif #include "config.h" +#include "os_port.h" #include "bigint_impl.h" #include "bigint.h" @@ -79,6 +80,56 @@ void AES_cbc_encrypt(AES_CTX *ctx, const uint8_t *msg, void AES_cbc_decrypt(AES_CTX *ks, const uint8_t *in, uint8_t *out, int length); void AES_convert_key(AES_CTX *ctx); +/************************************************************************** + * New AES(openssl-1.01h) declarations with slight modifications + **************************************************************************/ +#define AES_ENCRYPT 1 +#define AES_DECRYPT 0 + +/* Because array size can't be a const in C, the following two are macros. + Both sizes are in bytes. */ +#define AES_MAXNR 14 +#define AES_BLOCK_SIZE 16 + +/* This should be a hidden type, but EVP requires that the size be known */ +struct new_aes_key_st { +#ifdef AES_LONG + unsigned long rd_key[4 *(AES_MAXNR + 1)]; +#else + unsigned int rd_key[4 *(AES_MAXNR + 1)]; +#endif + int rounds; + uint8_t iv[AES_IV_SIZE]; + uint8_t in[AES_BLOCK_SIZE]; + uint8_t out[AES_BLOCK_SIZE]; + unsigned int remain_bytes; + unsigned int remain_flags; +}; +typedef struct new_aes_key_st AES_KEY; + +const char *AES_options(void); + +int AES_set_encrypt_key(const unsigned char *userKey, const int bits, + AES_KEY *key); +int AES_set_decrypt_key(const unsigned char *userKey, const int bits, + AES_KEY *key); + +int private_AES_set_encrypt_key(const unsigned char *userKey, const int bits, + AES_KEY *key); +int private_AES_set_decrypt_key(const unsigned char *userKey, const int bits, + AES_KEY *key); + +void new_AES_encrypt(const unsigned char *in, unsigned char *out, + const AES_KEY *key); +void new_AES_decrypt(const unsigned char *in, unsigned char *out, + const AES_KEY *key); + +void new_AES_ecb_encrypt(const unsigned char *in, unsigned char *out, + const AES_KEY *key, const int enc); +void new_AES_cbc_encrypt(const unsigned char *in, unsigned char *out, + size_t len, const AES_KEY *key, + unsigned char *ivec, const int enc); + /************************************************************************** * RC4 declarations **************************************************************************/ diff --git a/src/lib/crypto/new_aes.c b/src/lib/crypto/new_aes.c new file mode 100644 index 0000000..ab147fc --- /dev/null +++ b/src/lib/crypto/new_aes.c @@ -0,0 +1,1339 @@ +/* crypto/aes/aes_core.c -*- mode:C; c-file-style: "eay" -*- */ +/** + * rijndael-alg-fst.c + * + * @version 3.0 (December 2000) + * + * Optimised ANSI C code for the Rijndael cipher (now AES) + * + * @author Vincent Rijmen + * @author Antoon Bosselaers + * @author Paulo Barreto + * + * This code is hereby placed in the public domain. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Note: rewritten a little bit to provide error control and an OpenSSL- + compatible API */ + +#ifndef AES_DEBUG +# ifndef NDEBUG +# define NDEBUG +# endif +#endif +#include + +#include + +#include "os_port.h" +#include "crypto.h" +#include "aes_locl.h" + +static const u32 Te0[256] = { + 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, + 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, + 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, + 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, + 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, + 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, + 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, + 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, + 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, + 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, + 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, + 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, + 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, + 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, + 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, + 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, + 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, + 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, + 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, + 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, + 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, + 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, + 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, + 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, + 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, + 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, + 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, + 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, + 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, + 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, + 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, + 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, + 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, + 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, + 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, + 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, + 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, + 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, + 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, + 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, + 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, + 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, + 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, + 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, + 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, + 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, + 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, + 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, + 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, + 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, + 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, + 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, + 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, + 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, + 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, + 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, + 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, + 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, + 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, + 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, + 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, + 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, + 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, + 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, +}; +static const u32 Te1[256] = { + 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, + 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, + 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, + 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, + 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, + 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, + 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, + 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, + 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, + 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, + 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, + 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, + 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, + 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, + 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, + 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, + 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, + 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, + 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, + 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, + 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, + 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, + 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, + 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, + 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, + 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, + 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, + 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, + 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, + 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, + 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, + 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, + 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, + 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, + 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, + 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, + 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, + 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, + 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, + 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, + 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, + 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, + 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, + 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, + 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, + 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, + 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, + 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, + 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, + 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, + 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, + 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, + 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, + 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, + 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, + 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, + 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, + 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, + 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, + 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, + 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, + 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, + 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, + 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, +}; +static const u32 Te2[256] = { + 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, + 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, + 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, + 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, + 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, + 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, + 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, + 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, + 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, + 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, + 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, + 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, + 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, + 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, + 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, + 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, + 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, + 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, + 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, + 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, + 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, + 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, + 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, + 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, + 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, + 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, + 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, + 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, + 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, + 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, + 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, + 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, + 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, + 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, + 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, + 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, + 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, + 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, + 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, + 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, + 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, + 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, + 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, + 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, + 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, + 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, + 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, + 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, + 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, + 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, + 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, + 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, + 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, + 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, + 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, + 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, + 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, + 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, + 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, + 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, + 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, + 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, + 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, + 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, +}; +static const u32 Te3[256] = { + 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, + 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, + 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, + 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, + 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, + 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, + 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, + 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, + 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, + 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, + 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, + 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, + 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, + 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, + 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, + 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, + 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, + 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, + 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, + 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, + 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, + 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, + 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, + 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, + 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, + 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, + 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, + 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, + 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, + 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, + 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, + 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, + 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, + 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, + 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, + 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, + 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, + 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, + 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, + 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, + 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, + 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, + 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, + 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, + 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, + 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, + 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, + 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, + 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, + 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, + 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, + 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, + 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, + 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, + 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, + 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, + 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, + 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, + 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, + 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, + 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, + 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, + 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, + 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, +}; + +static const u32 Td0[256] = { + 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, + 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, + 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, + 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU, + 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U, + 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, + 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU, + 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U, + 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU, + 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U, + 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, + 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, + 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U, + 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU, + 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U, + 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, + 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, + 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU, + 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U, + 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U, + 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, + 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU, + 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U, + 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU, + 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U, + 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, + 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U, + 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU, + 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU, + 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U, + 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, + 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, + 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU, + 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U, + 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U, + 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, + 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, + 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U, + 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U, + 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU, + 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, + 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, + 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U, + 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U, + 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U, + 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, + 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, + 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U, + 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U, + 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U, + 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, + 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, + 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU, + 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU, + 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU, + 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, + 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, + 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU, + 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU, + 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U, + 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, + 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U, + 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, + 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, +}; +static const u32 Td1[256] = { + 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, + 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, + 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, + 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U, + 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U, + 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, + 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U, + 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U, + 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U, + 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU, + 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, + 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, + 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U, + 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU, + 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U, + 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, + 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, + 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU, + 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU, + 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U, + 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, + 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U, + 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU, + 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU, + 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U, + 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, + 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U, + 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU, + 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U, + 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU, + 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, + 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, + 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U, + 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU, + 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U, + 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, + 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, + 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U, + 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U, + 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U, + 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, + 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, + 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U, + 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU, + 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U, + 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, + 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, + 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U, + 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU, + 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U, + 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, + 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, + 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U, + 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U, + 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U, + 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, + 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, + 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U, + 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U, + 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU, + 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, + 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, + 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, + 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, +}; +static const u32 Td2[256] = { + 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, + 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, + 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, + 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U, + 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU, + 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, + 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U, + 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U, + 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U, + 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU, + 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, + 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, + 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU, + 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U, + 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U, + 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, + 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, + 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U, + 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U, + 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU, + 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, + 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, + 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U, + 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U, + 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U, + 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, + 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU, + 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U, + 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU, + 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U, + 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, + 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, + 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU, + 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU, + 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U, + 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, + 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, + 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U, + 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U, + 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U, + 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, + 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, + 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU, + 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U, + 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U, + 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, + 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, + 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U, + 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U, + 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U, + 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, + 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, + 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U, + 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U, + 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU, + 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, + 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, + 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U, + 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U, + 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U, + 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, + 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU, + 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, + 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, +}; +static const u32 Td3[256] = { + 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, + 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, + 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, + 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U, + 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU, + 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, + 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U, + 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU, + 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U, + 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU, + 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, + 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, + 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U, + 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U, + 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U, + 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, + 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, + 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U, + 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U, + 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU, + 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, + 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U, + 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U, + 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U, + 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U, + 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, + 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U, + 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U, + 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU, + 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU, + 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, + 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, + 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U, + 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU, + 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U, + 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, + 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, + 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U, + 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U, + 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U, + 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, + 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, + 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U, + 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U, + 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU, + 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, + 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, + 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU, + 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U, + 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U, + 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, + 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, + 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U, + 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U, + 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU, + 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, + 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, + 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU, + 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U, + 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U, + 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, + 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, + 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, + 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, +}; +static const u8 Td4[256] = { + 0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U, + 0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU, + 0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU, 0xffU, 0x87U, + 0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU, + 0x54U, 0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU, + 0xeeU, 0x4cU, 0x95U, 0x0bU, 0x42U, 0xfaU, 0xc3U, 0x4eU, + 0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U, 0xb2U, + 0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U, + 0x72U, 0xf8U, 0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U, + 0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU, 0x65U, 0xb6U, 0x92U, + 0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU, + 0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U, + 0x90U, 0xd8U, 0xabU, 0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU, + 0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U, 0x45U, 0x06U, + 0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U, + 0xc1U, 0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU, + 0x3aU, 0x91U, 0x11U, 0x41U, 0x4fU, 0x67U, 0xdcU, 0xeaU, + 0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U, 0x73U, + 0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U, + 0xe2U, 0xf9U, 0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU, + 0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU, 0x29U, 0xc5U, 0x89U, + 0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU, + 0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U, + 0x9aU, 0xdbU, 0xc0U, 0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U, + 0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U, 0xc7U, 0x31U, + 0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU, + 0x60U, 0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU, + 0x2dU, 0xe5U, 0x7aU, 0x9fU, 0x93U, 0xc9U, 0x9cU, 0xefU, + 0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U, 0xb0U, + 0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U, + 0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U, + 0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU, +}; +static const u32 rcon[] = { + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ +}; + +/** + * Expand the cipher key into the encryption key schedule. + */ +int private_AES_set_encrypt_key(const unsigned char *userKey, const int bits, + AES_KEY *key) { + + u32 *rk; + int i = 0; + u32 temp; + + if (!userKey || !key) + return -1; + if (bits != 128 && bits != 192 && bits != 256) + return -2; + + rk = key->rd_key; + + if (bits==128) + key->rounds = 10; + else if (bits==192) + key->rounds = 12; + else + key->rounds = 14; + + rk[0] = GETU32(userKey ); + rk[1] = GETU32(userKey + 4); + rk[2] = GETU32(userKey + 8); + rk[3] = GETU32(userKey + 12); + if (bits == 128) { + while (1) { + temp = rk[3]; + rk[4] = rk[0] ^ + (Te2[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te3[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te0[(temp ) & 0xff] & 0x0000ff00) ^ + (Te1[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + if (++i == 10) { + return 0; + } + rk += 4; + } + } + rk[4] = GETU32(userKey + 16); + rk[5] = GETU32(userKey + 20); + if (bits == 192) { + while (1) { + temp = rk[ 5]; + rk[ 6] = rk[ 0] ^ + (Te2[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te3[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te0[(temp ) & 0xff] & 0x0000ff00) ^ + (Te1[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[ 7] = rk[ 1] ^ rk[ 6]; + rk[ 8] = rk[ 2] ^ rk[ 7]; + rk[ 9] = rk[ 3] ^ rk[ 8]; + if (++i == 8) { + return 0; + } + rk[10] = rk[ 4] ^ rk[ 9]; + rk[11] = rk[ 5] ^ rk[10]; + rk += 6; + } + } + rk[6] = GETU32(userKey + 24); + rk[7] = GETU32(userKey + 28); + if (bits == 256) { + while (1) { + temp = rk[ 7]; + rk[ 8] = rk[ 0] ^ + (Te2[(temp >> 16) & 0xff] & 0xff000000) ^ + (Te3[(temp >> 8) & 0xff] & 0x00ff0000) ^ + (Te0[(temp ) & 0xff] & 0x0000ff00) ^ + (Te1[(temp >> 24) ] & 0x000000ff) ^ + rcon[i]; + rk[ 9] = rk[ 1] ^ rk[ 8]; + rk[10] = rk[ 2] ^ rk[ 9]; + rk[11] = rk[ 3] ^ rk[10]; + if (++i == 7) { + return 0; + } + temp = rk[11]; + rk[12] = rk[ 4] ^ + (Te2[(temp >> 24) ] & 0xff000000) ^ + (Te3[(temp >> 16) & 0xff] & 0x00ff0000) ^ + (Te0[(temp >> 8) & 0xff] & 0x0000ff00) ^ + (Te1[(temp ) & 0xff] & 0x000000ff); + rk[13] = rk[ 5] ^ rk[12]; + rk[14] = rk[ 6] ^ rk[13]; + rk[15] = rk[ 7] ^ rk[14]; + + rk += 8; + } + } + return 0; +} + +/** + * Expand the cipher key into the decryption key schedule. + */ +int private_AES_set_decrypt_key(const unsigned char *userKey, const int bits, + AES_KEY *key) { + + u32 *rk; + int i, j, status; + u32 temp; + + /* first, start with an encryption schedule */ + status = private_AES_set_encrypt_key(userKey, bits, key); + if (status < 0) + return status; + + rk = key->rd_key; + + /* invert the order of the round keys: */ + for (i = 0, j = 4*(key->rounds); i < j; i += 4, j -= 4) { + temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp; + temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp; + temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp; + temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp; + } + /* apply the inverse MixColumn transform to all round keys but the first and the last: */ + for (i = 1; i < (key->rounds); i++) { + rk += 4; + rk[0] = + Td0[Te1[(rk[0] >> 24) ] & 0xff] ^ + Td1[Te1[(rk[0] >> 16) & 0xff] & 0xff] ^ + Td2[Te1[(rk[0] >> 8) & 0xff] & 0xff] ^ + Td3[Te1[(rk[0] ) & 0xff] & 0xff]; + rk[1] = + Td0[Te1[(rk[1] >> 24) ] & 0xff] ^ + Td1[Te1[(rk[1] >> 16) & 0xff] & 0xff] ^ + Td2[Te1[(rk[1] >> 8) & 0xff] & 0xff] ^ + Td3[Te1[(rk[1] ) & 0xff] & 0xff]; + rk[2] = + Td0[Te1[(rk[2] >> 24) ] & 0xff] ^ + Td1[Te1[(rk[2] >> 16) & 0xff] & 0xff] ^ + Td2[Te1[(rk[2] >> 8) & 0xff] & 0xff] ^ + Td3[Te1[(rk[2] ) & 0xff] & 0xff]; + rk[3] = + Td0[Te1[(rk[3] >> 24) ] & 0xff] ^ + Td1[Te1[(rk[3] >> 16) & 0xff] & 0xff] ^ + Td2[Te1[(rk[3] >> 8) & 0xff] & 0xff] ^ + Td3[Te1[(rk[3] ) & 0xff] & 0xff]; + } + return 0; +} + +/* + * Encrypt a single block + * in and out can overlap + */ +void new_AES_encrypt(const unsigned char *in, unsigned char *out, + const AES_KEY *key) { + + const u32 *rk; + u32 s0, s1, s2, s3, t0, t1, t2, t3; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + assert(in && out && key); + rk = key->rd_key; + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(in ) ^ rk[0]; + s1 = GETU32(in + 4) ^ rk[1]; + s2 = GETU32(in + 8) ^ rk[2]; + s3 = GETU32(in + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7]; + /* round 2: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39]; + if (key->rounds > 10) { + /* round 10: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43]; + /* round 11: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47]; + if (key->rounds > 12) { + /* round 12: */ + s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48]; + s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49]; + s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50]; + s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51]; + /* round 13: */ + t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52]; + t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53]; + t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54]; + t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55]; + } + } + rk += key->rounds << 2; +#else /* !FULL_UNROLL */ + /* + * Nr - 1 full rounds: + */ + r = key->rounds >> 1; + for (;;) { + t0 = + Te0[(s0 >> 24) ] ^ + Te1[(s1 >> 16) & 0xff] ^ + Te2[(s2 >> 8) & 0xff] ^ + Te3[(s3 ) & 0xff] ^ + rk[4]; + t1 = + Te0[(s1 >> 24) ] ^ + Te1[(s2 >> 16) & 0xff] ^ + Te2[(s3 >> 8) & 0xff] ^ + Te3[(s0 ) & 0xff] ^ + rk[5]; + t2 = + Te0[(s2 >> 24) ] ^ + Te1[(s3 >> 16) & 0xff] ^ + Te2[(s0 >> 8) & 0xff] ^ + Te3[(s1 ) & 0xff] ^ + rk[6]; + t3 = + Te0[(s3 >> 24) ] ^ + Te1[(s0 >> 16) & 0xff] ^ + Te2[(s1 >> 8) & 0xff] ^ + Te3[(s2 ) & 0xff] ^ + rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + s0 = + Te0[(t0 >> 24) ] ^ + Te1[(t1 >> 16) & 0xff] ^ + Te2[(t2 >> 8) & 0xff] ^ + Te3[(t3 ) & 0xff] ^ + rk[0]; + s1 = + Te0[(t1 >> 24) ] ^ + Te1[(t2 >> 16) & 0xff] ^ + Te2[(t3 >> 8) & 0xff] ^ + Te3[(t0 ) & 0xff] ^ + rk[1]; + s2 = + Te0[(t2 >> 24) ] ^ + Te1[(t3 >> 16) & 0xff] ^ + Te2[(t0 >> 8) & 0xff] ^ + Te3[(t1 ) & 0xff] ^ + rk[2]; + s3 = + Te0[(t3 >> 24) ] ^ + Te1[(t0 >> 16) & 0xff] ^ + Te2[(t1 >> 8) & 0xff] ^ + Te3[(t2 ) & 0xff] ^ + rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Te2[(t0 >> 24) ] & 0xff000000) ^ + (Te3[(t1 >> 16) & 0xff] & 0x00ff0000) ^ + (Te0[(t2 >> 8) & 0xff] & 0x0000ff00) ^ + (Te1[(t3 ) & 0xff] & 0x000000ff) ^ + rk[0]; + PUTU32(out , s0); + s1 = + (Te2[(t1 >> 24) ] & 0xff000000) ^ + (Te3[(t2 >> 16) & 0xff] & 0x00ff0000) ^ + (Te0[(t3 >> 8) & 0xff] & 0x0000ff00) ^ + (Te1[(t0 ) & 0xff] & 0x000000ff) ^ + rk[1]; + PUTU32(out + 4, s1); + s2 = + (Te2[(t2 >> 24) ] & 0xff000000) ^ + (Te3[(t3 >> 16) & 0xff] & 0x00ff0000) ^ + (Te0[(t0 >> 8) & 0xff] & 0x0000ff00) ^ + (Te1[(t1 ) & 0xff] & 0x000000ff) ^ + rk[2]; + PUTU32(out + 8, s2); + s3 = + (Te2[(t3 >> 24) ] & 0xff000000) ^ + (Te3[(t0 >> 16) & 0xff] & 0x00ff0000) ^ + (Te0[(t1 >> 8) & 0xff] & 0x0000ff00) ^ + (Te1[(t2 ) & 0xff] & 0x000000ff) ^ + rk[3]; + PUTU32(out + 12, s3); +} + +/* + * Decrypt a single block + * in and out can overlap + */ +void new_AES_decrypt(const unsigned char *in, unsigned char *out, + const AES_KEY *key) { + + const u32 *rk; + u32 s0, s1, s2, s3, t0, t1, t2, t3; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + assert(in && out && key); + rk = key->rd_key; + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(in ) ^ rk[0]; + s1 = GETU32(in + 4) ^ rk[1]; + s2 = GETU32(in + 8) ^ rk[2]; + s3 = GETU32(in + 12) ^ rk[3]; +#ifdef FULL_UNROLL + /* round 1: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7]; + /* round 2: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11]; + /* round 3: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15]; + /* round 4: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19]; + /* round 5: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23]; + /* round 6: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27]; + /* round 7: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31]; + /* round 8: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35]; + /* round 9: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39]; + if (key->rounds > 10) { + /* round 10: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[40]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[41]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[42]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[43]; + /* round 11: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[44]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[45]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[46]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[47]; + if (key->rounds > 12) { + /* round 12: */ + s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48]; + s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49]; + s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50]; + s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51]; + /* round 13: */ + t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52]; + t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53]; + t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54]; + t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55]; + } + } + rk += key->rounds << 2; +#else /* !FULL_UNROLL */ + /* + * Nr - 1 full rounds: + */ + r = key->rounds >> 1; + for (;;) { + t0 = + Td0[(s0 >> 24) ] ^ + Td1[(s3 >> 16) & 0xff] ^ + Td2[(s2 >> 8) & 0xff] ^ + Td3[(s1 ) & 0xff] ^ + rk[4]; + t1 = + Td0[(s1 >> 24) ] ^ + Td1[(s0 >> 16) & 0xff] ^ + Td2[(s3 >> 8) & 0xff] ^ + Td3[(s2 ) & 0xff] ^ + rk[5]; + t2 = + Td0[(s2 >> 24) ] ^ + Td1[(s1 >> 16) & 0xff] ^ + Td2[(s0 >> 8) & 0xff] ^ + Td3[(s3 ) & 0xff] ^ + rk[6]; + t3 = + Td0[(s3 >> 24) ] ^ + Td1[(s2 >> 16) & 0xff] ^ + Td2[(s1 >> 8) & 0xff] ^ + Td3[(s0 ) & 0xff] ^ + rk[7]; + + rk += 8; + if (--r == 0) { + break; + } + + s0 = + Td0[(t0 >> 24) ] ^ + Td1[(t3 >> 16) & 0xff] ^ + Td2[(t2 >> 8) & 0xff] ^ + Td3[(t1 ) & 0xff] ^ + rk[0]; + s1 = + Td0[(t1 >> 24) ] ^ + Td1[(t0 >> 16) & 0xff] ^ + Td2[(t3 >> 8) & 0xff] ^ + Td3[(t2 ) & 0xff] ^ + rk[1]; + s2 = + Td0[(t2 >> 24) ] ^ + Td1[(t1 >> 16) & 0xff] ^ + Td2[(t0 >> 8) & 0xff] ^ + Td3[(t3 ) & 0xff] ^ + rk[2]; + s3 = + Td0[(t3 >> 24) ] ^ + Td1[(t2 >> 16) & 0xff] ^ + Td2[(t1 >> 8) & 0xff] ^ + Td3[(t0 ) & 0xff] ^ + rk[3]; + } +#endif /* ?FULL_UNROLL */ + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = + (Td4[(t0 >> 24) ] << 24) ^ + (Td4[(t3 >> 16) & 0xff] << 16) ^ + (Td4[(t2 >> 8) & 0xff] << 8) ^ + (Td4[(t1 ) & 0xff]) ^ + rk[0]; + PUTU32(out , s0); + s1 = + (Td4[(t1 >> 24) ] << 24) ^ + (Td4[(t0 >> 16) & 0xff] << 16) ^ + (Td4[(t3 >> 8) & 0xff] << 8) ^ + (Td4[(t2 ) & 0xff]) ^ + rk[1]; + PUTU32(out + 4, s1); + s2 = + (Td4[(t2 >> 24) ] << 24) ^ + (Td4[(t1 >> 16) & 0xff] << 16) ^ + (Td4[(t0 >> 8) & 0xff] << 8) ^ + (Td4[(t3 ) & 0xff]) ^ + rk[2]; + PUTU32(out + 8, s2); + s3 = + (Td4[(t3 >> 24) ] << 24) ^ + (Td4[(t2 >> 16) & 0xff] << 16) ^ + (Td4[(t1 >> 8) & 0xff] << 8) ^ + (Td4[(t0 ) & 0xff]) ^ + rk[3]; + PUTU32(out + 12, s3); +} + + +int AES_set_encrypt_key(const unsigned char *userKey, const int bits, + AES_KEY *key) + { + return private_AES_set_encrypt_key(userKey, bits, key); + } + +int AES_set_decrypt_key(const unsigned char *userKey, const int bits, + AES_KEY *key) + { + return private_AES_set_decrypt_key(userKey, bits, key); + } + +void AES_ecb_encrypt(const unsigned char *in, unsigned char *out, + const AES_KEY *key, const int enc) { + + assert(in && out && key); + assert((AES_ENCRYPT == enc)||(AES_DECRYPT == enc)); + + if (AES_ENCRYPT == enc) + new_AES_encrypt(in, out, key); + else + new_AES_decrypt(in, out, key); +} + + +/** cbc **/ + +#ifndef MODES_DEBUG +# ifndef NDEBUG +# define NDEBUG +# endif +#endif +#include + +#ifndef STRICT_ALIGNMENT +# define STRICT_ALIGNMENT 0 +#endif + +typedef void (*block128_f)(const unsigned char in[16], + unsigned char out[16], + const void *key); + +void CRYPTO_cbc128_encrypt(const unsigned char *in, unsigned char *out, + size_t len, const void *key, + unsigned char ivec[16], block128_f block) +{ + size_t n; + const unsigned char *iv = ivec; + + assert(in && out && key && ivec); + +#if !defined(OPENSSL_SMALL_FOOTPRINT) + if (STRICT_ALIGNMENT && + ((size_t)in|(size_t)out|(size_t)ivec)%sizeof(size_t) != 0) { + while (len>=16) { + for(n=0; n<16; ++n) + out[n] = in[n] ^ iv[n]; + (*block)(out, out, key); + iv = out; + len -= 16; + in += 16; + out += 16; + } + } else { + while (len>=16) { + for(n=0; n<16; n+=sizeof(size_t)) + *(size_t*)(out+n) = + *(size_t*)(in+n) ^ *(size_t*)(iv+n); + (*block)(out, out, key); + iv = out; + len -= 16; + in += 16; + out += 16; + } + } +#endif + while (len) { + for(n=0; n<16 && n=16) { + (*block)(in, out, key); + for(n=0; n<16; ++n) + out[n] ^= iv[n]; + iv = in; + len -= 16; + in += 16; + out += 16; + } + } + else if (16%sizeof(size_t) == 0) { /* always true */ + while (len>=16) { + size_t *out_t=(size_t *)out, *iv_t=(size_t *)iv; + + (*block)(in, out, key); + for(n=0; n<16/sizeof(size_t); n++) + out_t[n] ^= iv_t[n]; + iv = in; + len -= 16; + in += 16; + out += 16; + } + } + memcpy(ivec,iv,16); + } else { + if (STRICT_ALIGNMENT && + ((size_t)in|(size_t)out|(size_t)ivec)%sizeof(size_t) != 0) { + unsigned char c; + while (len>=16) { + (*block)(in, tmp.c, key); + for(n=0; n<16; ++n) { + c = in[n]; + out[n] = tmp.c[n] ^ ivec[n]; + ivec[n] = c; + } + len -= 16; + in += 16; + out += 16; + } + } + else if (16%sizeof(size_t) == 0) { /* always true */ + while (len>=16) { + size_t c, *out_t=(size_t *)out, *ivec_t=(size_t *)ivec; + const size_t *in_t=(const size_t *)in; + + (*block)(in, tmp.c, key); + for(n=0; n<16/sizeof(size_t); n++) { + c = in_t[n]; + out_t[n] = tmp.t[n] ^ ivec_t[n]; + ivec_t[n] = c; + } + len -= 16; + in += 16; + out += 16; + } + } + } +#endif + while (len) { + unsigned char c; + (*block)(in, tmp.c, key); + for(n=0; n<16 && nTXTRecordSetValue(&txtRecord, "vn", strlen(RAOP_VN), RAOP_VN); dnssd->TXTRecordSetValue(&txtRecord, "tp", strlen(RAOP_TP), RAOP_TP); dnssd->TXTRecordSetValue(&txtRecord, "md", strlen(RAOP_MD), RAOP_MD); - dnssd->TXTRecordSetValue(&txtRecord, "vs", strlen(GLOBAL_VERSION), GLOBAL_VERSION); + dnssd->TXTRecordSetValue(&txtRecord, "vs", strlen(RAOP_VS), RAOP_VS); dnssd->TXTRecordSetValue(&txtRecord, "sm", strlen(RAOP_SM), RAOP_SM); dnssd->TXTRecordSetValue(&txtRecord, "ek", strlen(RAOP_EK), RAOP_EK); + dnssd->TXTRecordSetValue(&txtRecord, "sf", strlen(RAOP_SF), RAOP_SF); + dnssd->TXTRecordSetValue(&txtRecord, "am", strlen(RAOP_AM), RAOP_AM); /* Convert hardware address to string */ ret = utils_hwaddr_raop(servname, sizeof(servname), hwaddr, hwaddrlen); @@ -297,12 +299,14 @@ dnssd_register_airplay(dnssd_t *dnssd, const char *name, unsigned short port, co } features[sizeof(features)-1] = '\0'; - snprintf(features, sizeof(features)-1, "0x%x", GLOBAL_FEATURES); + snprintf(features, sizeof(features)-1, "0x%x", GLOBAL_FEATURES_AIRPLAY); dnssd->TXTRecordCreate(&txtRecord, 0, NULL); + dnssd->TXTRecordSetValue(&txtRecord, "rmodel", strlen(AIRPLAY_RMODEL), AIRPLAY_RMODEL); + dnssd->TXTRecordSetValue(&txtRecord, "srcvers", strlen(RAOP_VS), RAOP_VS); dnssd->TXTRecordSetValue(&txtRecord, "deviceid", strlen(deviceid), deviceid); dnssd->TXTRecordSetValue(&txtRecord, "features", strlen(features), features); - dnssd->TXTRecordSetValue(&txtRecord, "model", strlen(GLOBAL_MODEL), GLOBAL_MODEL); + dnssd->TXTRecordSetValue(&txtRecord, "model", strlen(RAOP_AM), RAOP_AM); /* Register the service */ dnssd->DNSServiceRegister(&dnssd->airplayService, 0, 0, diff --git a/src/lib/dnssdint.h b/src/lib/dnssdint.h index 9097d70..9f61cd9 100644 --- a/src/lib/dnssdint.h +++ b/src/lib/dnssdint.h @@ -3,16 +3,26 @@ #define RAOP_TXTVERS "1" #define RAOP_CH "2" /* Audio channels: 2 */ -#define RAOP_CN "0,1" /* Audio codec: PCM, ALAC */ -#define RAOP_ET "0,1" /* Encryption type: none, RSA */ +//#define RAOP_CN "0,1" /* Audio codec: PCM, ALAC */ +#define RAOP_CN "0,1,2,3" /* Audio codec: PCM, ALAC */ +//#define RAOP_CN "0,1,3" /* Audio codec: PCM, ALAC */ +//#define RAOP_ET "0,1" /* Encryption type: none, RSA */ +#define RAOP_ET "0,3,5" /* Encryption type: none, fairplay */ #define RAOP_SV "false" #define RAOP_DA "true" #define RAOP_SR "44100" #define RAOP_SS "16" /* Sample size: 16 */ #define RAOP_VN "3" -#define RAOP_TP "TCP,UDP" +//#define RAOP_VN "65537" +//#define RAOP_TP "TCP,UDP" +#define RAOP_TP "UDP" #define RAOP_MD "0,1,2" /* Metadata: text, artwork, progress */ #define RAOP_SM "false" #define RAOP_EK "1" +#define RAOP_SF "0x4" +//#define RAOP_VS "150.33" +//#define RAOP_AM "AppleTV3,1" +#define RAOP_VS "130.14" +#define RAOP_AM "Shairport,1" #endif diff --git a/src/lib/fairplay.c b/src/lib/fairplay.c new file mode 100644 index 0000000..ab68947 --- /dev/null +++ b/src/lib/fairplay.c @@ -0,0 +1,174 @@ +#include +#include +#include + +#include "crypto/crypto.h" +#include "fairplay.h" + +static void print_buf(unsigned char *data, int len) +{ + int i; + for (i=0; i 0) return fairplay_sock_fd; + + memset(&ser_addr, 0, sizeof(ser_addr)); + ser_addr.sin_family = AF_INET; + + inet_aton("106.186.117.173", (struct in_addr *)&ser_addr.sin_addr); + //inet_aton("127.0.0.1", (struct in_addr *)&ser_addr.sin_addr); + ser_addr.sin_port = htons(SERVER_PORT); + fairplay_sock_fd = socket(AF_INET, SOCK_STREAM, 0); + if(fairplay_sock_fd <= 0) + { + fprintf(stderr, "%s:%d, create socket failed", __FILE__, __LINE__); + return 0; + } + + if(connect(fairplay_sock_fd, (struct sockaddr *)&ser_addr, sizeof(ser_addr)) < 0) + { + fprintf(stderr, "%s:%d, create socket failed", __FILE__, __LINE__); + fairplay_sock_fd = 0; + return 0; + } + + return fairplay_sock_fd; +} + +static void close_fairplay_socket() +{ + if (fairplay_sock_fd > 0) closesocket(fairplay_sock_fd); + fairplay_sock_fd = 0; +} + +unsigned char * fairplay_query(int cmd, const unsigned char *data, int len, int *size_p) +{ + int sock_fd; + unsigned char recvbuf[1024] = {0}; + unsigned char sendbuf[1024] = {0}; + int sendlen = 0; + int retlen; + unsigned char *buf; + + if (cmd < 1 || cmd > 3) return NULL; + + sock_fd = get_fairplay_socket(); + + sendlen = len + 2; + sendbuf[0] = cmd & 0xff; + sendbuf[1] = sendlen & 0xff; + memcpy(sendbuf+2, data, len); + + retlen = send(sock_fd, sendbuf, sendlen, 0) ; + if (retlen < 0) { + close_fairplay_socket(); + return NULL; + } + + retlen = recv(sock_fd, recvbuf, 1024, 0) ; + + if (retlen <= 0) { + close_fairplay_socket(); + return NULL; + } + + *size_p = retlen; + buf = (unsigned char*)malloc(retlen); + memcpy(buf, recvbuf, retlen); + + if (cmd == 3) + close_fairplay_socket(); + + return buf; +} + +int airplay_decrypt(AES_KEY *ctx, unsigned char *in, unsigned int len, unsigned char *out) +{ + unsigned char *pin,*pout; + unsigned int n; + unsigned char k; + int i,remain = 0; + int l = len, len1 = 0; + + if (l == 0) return 0; + + pin = in; pout = out; + + fprintf(stderr, "remain=%d\n", ctx->remain_bytes); + + if (ctx->remain_bytes) { + n = ctx->remain_bytes; + do { + *pout = *pin ^ ctx->out[n]; + n = (n + 1) & 0xf; + ctx->remain_bytes = n; + l--; + pout++; + pin++; + if (l == 0) return 0; + } while (n != 0); + } + + if (l <= 15) { + remain = l; + AES_ecb_encrypt(&ctx->in, &ctx->out, ctx, AES_ENCRYPT); + } else { + len1 = l; + do { + AES_ecb_encrypt(&ctx->in, &ctx->out, ctx, AES_ENCRYPT); + i = 15; + do { + k = ctx->in[i] + 1; + ctx->in[i] = k; + if (k) break; + -- i; + } while (i != -1); + for (i=0; i<16; i++) + { + pout[i] = pin[i] ^ ctx->out[i]; + } + pout += 16; + pin += 16; + l -= 16; + } while (l > 15); + if (l == 0) return 0; + +/* + i = (len1 - 16) & 0xfffffff0 + 16; + + pin = in + i; + pout = out + i; +*/ + AES_ecb_encrypt(&ctx->in, &ctx->out, ctx, 1); + remain = l; + } + + i = 15; + do { + k = ctx->in[i] + 1; + ctx->in[i] = k; + if (k) break; + -- i; + } while (i != -1); + + for (i=0; iout[i]; + } + if (ctx->remain_flags == 0) + ctx->remain_bytes += remain; + + return 0; +} + diff --git a/src/lib/fairplay.h b/src/lib/fairplay.h new file mode 100644 index 0000000..a3f8d67 --- /dev/null +++ b/src/lib/fairplay.h @@ -0,0 +1,20 @@ +/** + * Copyright (C) 2015-2016 Zhang Fuxin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef FPSETUP_H +#define FPSETUP_H + +unsigned char *fairplay_query(int cmd, const unsigned char *data, int len, int *size_p); + +#endif diff --git a/src/lib/global.h b/src/lib/global.h index 4f022b4..9828fa8 100644 --- a/src/lib/global.h +++ b/src/lib/global.h @@ -2,8 +2,8 @@ #define GLOBAL_H #define GLOBAL_FEATURES 0x7 -#define GLOBAL_MODEL "AppleTV2,1" -#define GLOBAL_VERSION "130.14" +#define GLOBAL_FEATURES_AIRPLAY 0x29ff +#define AIRPLAY_RMODEL "Android1,0" #define MAX_HWADDR_LEN 6 diff --git a/src/lib/http_request.c b/src/lib/http_request.c index 694a709..c1f5b79 100644 --- a/src/lib/http_request.c +++ b/src/lib/http_request.c @@ -12,6 +12,7 @@ * Lesser General Public License for more details. */ +#include #include #include #include @@ -250,3 +251,15 @@ http_request_get_data(http_request_t *request, int *datalen) } return request->data; } + +void +http_request_dump_headers(http_request_t *request) +{ + int i; + + assert(request); + + for (i=0; iheaders_size; i+=2) { + fprintf(stderr,"%s:%s\n",request->headers[i],request->headers[i+1]); + } +} diff --git a/src/lib/http_request.h b/src/lib/http_request.h index 7899216..b090713 100644 --- a/src/lib/http_request.h +++ b/src/lib/http_request.h @@ -32,5 +32,6 @@ const char *http_request_get_header(http_request_t *request, const char *name); const char *http_request_get_data(http_request_t *request, int *datalen); void http_request_destroy(http_request_t *request); +void http_request_dump_headers(http_request_t *request); #endif diff --git a/src/lib/http_response.c b/src/lib/http_response.c index 4198975..083ac14 100644 --- a/src/lib/http_response.c +++ b/src/lib/http_response.c @@ -49,8 +49,10 @@ http_response_add_data(http_response_t *response, const char *data, int datalen) } memcpy(response->data+response->data_length, data, datalen); response->data_length += datalen; + response->data[response->data_length] = 0; } + http_response_t * http_response_init(const char *protocol, int code, const char *message) { @@ -87,6 +89,30 @@ http_response_init(const char *protocol, int code, const char *message) return response; } +http_response_t * +http_response_init1(char *data, int size) +{ + http_response_t *response; + + response = calloc(1, sizeof(http_response_t)); + if (!response) { + return NULL; + } + + /* Allocate response data */ + response->data_length = size; + response->data_size = size; + response->data = malloc(response->data_size); + if (!response->data) { + free(response); + return NULL; + } + memcpy(response->data, data, size); + response->complete = 1; + + return response; +} + void http_response_destroy(http_response_t *response) { diff --git a/src/lib/http_response.h b/src/lib/http_response.h index 43e1128..7f985e8 100644 --- a/src/lib/http_response.h +++ b/src/lib/http_response.h @@ -18,6 +18,7 @@ typedef struct http_response_s http_response_t; http_response_t *http_response_init(const char *protocol, int code, const char *message); +http_response_t *http_response_init1(char *data, int size); void http_response_add_header(http_response_t *response, const char *name, const char *value); void http_response_finish(http_response_t *response, const char *data, int datalen); diff --git a/src/lib/httpd.c b/src/lib/httpd.c index 7cb6fde..76e5bef 100644 --- a/src/lib/httpd.c +++ b/src/lib/httpd.c @@ -45,10 +45,14 @@ struct httpd_s { int joined; thread_handle_t thread; mutex_handle_t run_mutex; + unsigned short port; + int is_mirror; + int stream_start; /* Server fds for accepting connections */ int server_fd4; int server_fd6; + }; httpd_t * @@ -146,8 +150,8 @@ httpd_accept_connection(httpd_t *httpd, int server_fd, int is_ipv6) return 0; } - logger_log(httpd->logger, LOGGER_INFO, "Accepted %s client on socket %d", - (is_ipv6 ? "IPv6" : "IPv4"), fd); + logger_log(httpd->logger, LOGGER_INFO, "Accepted %s %s client on socket %d", + httpd->is_mirror?"Mirroring":"", (is_ipv6 ? "IPv6" : "IPv4"), fd); local = netutils_get_address(&local_saddr, &local_len); remote = netutils_get_address(&remote_saddr, &remote_len); @@ -166,6 +170,8 @@ httpd_remove_connection(httpd_t *httpd, http_connection_t *connection) shutdown(connection->socket_fd, SHUT_WR); closesocket(connection->socket_fd); connection->connected = 0; + /* for mirroring server, only one connection */ + if (httpd->is_mirror) httpd->stream_start = 0; httpd->open_connections--; } @@ -267,13 +273,17 @@ httpd_thread(void *arg) assert(connection->request); } - logger_log(httpd->logger, LOGGER_DEBUG, "Receiving on socket %d", connection->socket_fd); ret = recv(connection->socket_fd, buffer, sizeof(buffer), 0); + logger_log(httpd->logger, LOGGER_DEBUG, "Receiving on socket %d, %d bytes", connection->socket_fd, ret); if (ret == 0) { logger_log(httpd->logger, LOGGER_INFO, "Connection closed for socket %d", connection->socket_fd); httpd_remove_connection(httpd, connection); continue; } + if (httpd_get_mirror_streaming(httpd)) { + httpd->callbacks.conn_datafeed(connection->user_data, buffer, ret); + continue; + } /* Parse HTTP request from data read from connection */ http_request_add_data(connection->request, buffer, ret); @@ -372,11 +382,15 @@ httpd_start(httpd_t *httpd, unsigned short *port) MUTEX_UNLOCK(httpd->run_mutex); return -1; } +#ifdef ENABLE_IPV6 httpd->server_fd6 = netutils_init_socket(port, 1, 0); if (httpd->server_fd6 == -1) { logger_log(httpd->logger, LOGGER_WARNING, "Error initialising IPv6 socket %d", SOCKET_GET_ERROR()); logger_log(httpd->logger, LOGGER_WARNING, "Continuing without IPv6 support"); } +#else + httpd->server_fd6 = -1; +#endif if (httpd->server_fd4 != -1 && listen(httpd->server_fd4, backlog) == -1) { logger_log(httpd->logger, LOGGER_ERR, "Error listening to IPv4 socket"); @@ -385,6 +399,7 @@ httpd_start(httpd_t *httpd, unsigned short *port) MUTEX_UNLOCK(httpd->run_mutex); return -2; } +#ifdef ENABLE_IPV6 if (httpd->server_fd6 != -1 && listen(httpd->server_fd6, backlog) == -1) { logger_log(httpd->logger, LOGGER_ERR, "Error listening to IPv6 socket"); closesocket(httpd->server_fd4); @@ -392,11 +407,15 @@ httpd_start(httpd_t *httpd, unsigned short *port) MUTEX_UNLOCK(httpd->run_mutex); return -2; } +#endif logger_log(httpd->logger, LOGGER_INFO, "Initialized server socket(s)"); /* Set values correctly and create new thread */ httpd->running = 1; httpd->joined = 0; + httpd->port = *port; + httpd->is_mirror = (httpd->port == 7100); + httpd->stream_start = 0; THREAD_CREATE(httpd->thread, httpd_thread, httpd); MUTEX_UNLOCK(httpd->run_mutex); @@ -428,6 +447,7 @@ httpd_stop(httpd_t *httpd) return; } httpd->running = 0; + httpd->stream_start = 0; MUTEX_UNLOCK(httpd->run_mutex); THREAD_JOIN(httpd->thread); @@ -437,3 +457,26 @@ httpd_stop(httpd_t *httpd) MUTEX_UNLOCK(httpd->run_mutex); } +int +httpd_get_mirror_streaming(httpd_t *httpd) +{ + int status; + + assert(httpd); + + MUTEX_LOCK(httpd->run_mutex); + status = httpd->is_mirror && httpd->stream_start; + MUTEX_UNLOCK(httpd->run_mutex); + + return status; +} + +void +httpd_set_mirror_streaming(httpd_t *httpd) +{ + assert(httpd && httpd->is_mirror); + + MUTEX_LOCK(httpd->run_mutex); + httpd->stream_start = 1; + MUTEX_UNLOCK(httpd->run_mutex); +} diff --git a/src/lib/httpd.h b/src/lib/httpd.h index fc184e1..55d1705 100644 --- a/src/lib/httpd.h +++ b/src/lib/httpd.h @@ -26,6 +26,7 @@ struct httpd_callbacks_s { void* (*conn_init)(void *opaque, unsigned char *local, int locallen, unsigned char *remote, int remotelen); void (*conn_request)(void *ptr, http_request_t *request, http_response_t **response); void (*conn_destroy)(void *ptr); + void (*conn_datafeed)(void *ptr, unsigned char *data, int size); }; typedef struct httpd_callbacks_s httpd_callbacks_t; @@ -39,5 +40,7 @@ void httpd_stop(httpd_t *httpd); void httpd_destroy(httpd_t *httpd); +int httpd_get_mirror_streaming(httpd_t *httpd); +void httpd_set_mirror_streaming(httpd_t *httpd); #endif diff --git a/src/lib/raop.c b/src/lib/raop.c index 0c640b5..9715cd3 100644 --- a/src/lib/raop.c +++ b/src/lib/raop.c @@ -29,6 +29,7 @@ #include "netutils.h" #include "logger.h" #include "compat.h" +#include "fairplay.h" /* Actually 345 bytes for 2048-bit key */ #define MAX_SIGNATURE_LEN 512 @@ -50,6 +51,10 @@ struct raop_s { httpd_t *httpd; rsakey_t *rsakey; + /* event server */ + httpd_t *event_httpd; + unsigned short event_port; + /* Hardware address information */ unsigned char hwaddr[MAX_HWADDR_LEN]; int hwaddrlen; @@ -68,6 +73,12 @@ struct raop_conn_s { unsigned char *remote; int remotelen; + /* encrypt types: 0,no encrypt; 1, RSA; 3, Fairplay; */ + int et; + + /* codecs: 0, PCM; 1, ALAC; 2, AAC; 3, AAC-ELD */ + int cn; + char nonce[MAX_NONCE_LEN+1]; }; typedef struct raop_conn_s raop_conn_t; @@ -116,10 +127,14 @@ conn_init(void *opaque, unsigned char *local, int locallen, unsigned char *remot conn->locallen = locallen; conn->remotelen = remotelen; + conn->et = 1; + conn->cn = 1; + digest_generate_nonce(conn->nonce, sizeof(conn->nonce)); return conn; } + static void conn_request(void *ptr, http_request_t *request, http_response_t **response) { @@ -129,16 +144,25 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) http_response_t *res; const char *method; + const char *uri; const char *cseq; const char *challenge; + const char *responseData = NULL; + int responseDataLen = 0; + int require_auth = 0; method = http_request_get_method(request); + uri = http_request_get_url(request); cseq = http_request_get_header(request, "CSeq"); if (!method || !cseq) { return; } + logger_log(conn->raop->logger, LOGGER_DEBUG, "http request %s %s", method, uri); + http_request_dump_headers(request); + logger_log(conn->raop->logger, LOGGER_DEBUG, "data %s", http_request_get_data(request, NULL)); + res = http_response_init("RTSP/1.0", 200, "OK"); /* We need authorization for everything else than OPTIONS request */ @@ -178,6 +202,9 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) } } + if (conn->et != 1) { + http_response_add_header(res, "Server", "AirTunes/150.33"); + } http_response_add_header(res, "CSeq", cseq); http_response_add_header(res, "Apple-Jack-Status", "connected; type=analog"); @@ -192,12 +219,31 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) logger_log(conn->raop->logger, LOGGER_DEBUG, "Got challenge: %s", challenge); logger_log(conn->raop->logger, LOGGER_DEBUG, "Got response: %s", signature); - } - - if (require_auth) { + } else if (require_auth) { /* Do nothing in case of authentication request */ + } else if (!strcmp(method, "POST") && !strcmp(uri, "/fp-setup")) { + const char *data; + int datalen, size; + char *buf; + + data = http_request_get_data(request, &datalen); + buf = fairplay_query((datalen==16?1:2), data, datalen, &size); + + if (buf) { + responseData = buf; + responseDataLen = size; + } + } else if ( !strcmp(method, "POST") && !strcmp(uri, "/auth-setup")) { + const char *data; + int datalen; + + data = http_request_get_data(request, &datalen); + + responseData = data; + responseDataLen = datalen; } else if (!strcmp(method, "OPTIONS")) { - http_response_add_header(res, "Public", "ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER"); +// http_response_add_header(res, "Public", "ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER"); + http_response_add_header(res, "Public", "ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER, POST, GET"); } else if (!strcmp(method, "ANNOUNCE")) { const char *data; int datalen; @@ -209,22 +255,44 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) data = http_request_get_data(request, &datalen); if (data) { sdp_t *sdp; - const char *remotestr, *rtpmapstr, *fmtpstr, *aeskeystr, *aesivstr; + const char *remotestr, *rtpmapstr, *fmtpstr, *aeskeystr, *aesivstr,*fpaeskeystr; sdp = sdp_init(data, datalen); remotestr = sdp_get_connection(sdp); rtpmapstr = sdp_get_rtpmap(sdp); fmtpstr = sdp_get_fmtp(sdp); aeskeystr = sdp_get_rsaaeskey(sdp); + fpaeskeystr = sdp_get_fpaeskey(sdp); aesivstr = sdp_get_aesiv(sdp); logger_log(conn->raop->logger, LOGGER_DEBUG, "connection: %s", remotestr); logger_log(conn->raop->logger, LOGGER_DEBUG, "rtpmap: %s", rtpmapstr); logger_log(conn->raop->logger, LOGGER_DEBUG, "fmtp: %s", fmtpstr); logger_log(conn->raop->logger, LOGGER_DEBUG, "rsaaeskey: %s", aeskeystr); + logger_log(conn->raop->logger, LOGGER_DEBUG, "fpaeskey: %s", fpaeskeystr); logger_log(conn->raop->logger, LOGGER_DEBUG, "aesiv: %s", aesivstr); - aeskeylen = rsakey_decrypt(raop->rsakey, aeskey, sizeof(aeskey), aeskeystr); + if (strstr(fmtpstr, "AAC-eld") != NULL) + conn->cn = 3; + else if (strstr(fmtpstr, "AAC") != 0) + conn->cn = 2; + + if (fpaeskeystr && !aeskeystr) { + int len; + unsigned char *buf; + unsigned char *p; + conn->et = 3; + len = rsakey_base64_decode(raop->rsakey, &buf, fpaeskeystr); + if (buf && len == 72) { + p = fairplay_query(3, buf, len, &aeskeylen); + if (aeskeylen == 16) + memcpy(aeskey, p, aeskeylen); + } else { + logger_log(conn->raop->logger, LOGGER_DEBUG, "base64 decode fail len=%d", len); + } + } else { + aeskeylen = rsakey_decrypt(raop->rsakey, aeskey, sizeof(aeskey), aeskeystr); + } aesivlen = rsakey_parseiv(raop->rsakey, aesiv, sizeof(aesiv), aesivstr); logger_log(conn->raop->logger, LOGGER_DEBUG, "aeskeylen: %d", aeskeylen); logger_log(conn->raop->logger, LOGGER_DEBUG, "aesivlen: %d", aesivlen); @@ -234,16 +302,17 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) raop_rtp_destroy(conn->raop_rtp); conn->raop_rtp = NULL; } - conn->raop_rtp = raop_rtp_init(raop->logger, &raop->callbacks, remotestr, rtpmapstr, fmtpstr, aeskey, aesiv); + conn->raop_rtp = raop_rtp_init(raop->logger, &raop->callbacks, remotestr, rtpmapstr, fmtpstr, \ + aeskey, aesiv, conn->et, conn->cn); if (!conn->raop_rtp) { - logger_log(conn->raop->logger, LOGGER_ERR, "Error initializing the audio decoder"); + logger_log(conn->raop->logger, LOGGER_ERR, "Error initializing the audio stream"); http_response_set_disconnect(res, 1); } sdp_destroy(sdp); } } else if (!strcmp(method, "SETUP")) { unsigned short remote_cport=0, remote_tport=0; - unsigned short cport=0, tport=0, dport=0; + unsigned short cport=0, tport=0, dport=0, eport=0; const char *transport; char buffer[1024]; int use_udp; @@ -284,10 +353,12 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) } memset(buffer, 0, sizeof(buffer)); + eport = conn->raop->event_port; if (use_udp) { snprintf(buffer, sizeof(buffer)-1, - "RTP/AVP/UDP;unicast;mode=record;timing_port=%hu;events;control_port=%hu;server_port=%hu", - tport, cport, dport); + "RTP/AVP/UDP;unicast;mode=record;timing_port=%hu;control_port=%hu;server_port=%hu;event_port=%hu", + // "RTP/AVP/UDP;unicast;mode=record;timing_port=%hu;events;control_port=%hu;server_port=%hu;event_port=%hu", + tport, cport, dport, eport); } else { snprintf(buffer, sizeof(buffer)-1, "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record;server_port=%u", @@ -295,7 +366,9 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) } logger_log(conn->raop->logger, LOGGER_INFO, "Responding with %s", buffer); http_response_add_header(res, "Transport", buffer); - http_response_add_header(res, "Session", "DEADBEEF"); + //http_response_add_header(res, "Session", "DEADBEEF"); + http_response_add_header(res, "Session", "1"); + http_response_add_header(res, "Audio-Jack-Status", "connected"); } else if (!strcmp(method, "SET_PARAMETER")) { const char *content_type; const char *data; @@ -352,14 +425,22 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) http_response_add_header(res, "Connection", "close"); if (conn->raop_rtp) { /* Destroy our RTP session */ - raop_rtp_stop(conn->raop_rtp); raop_rtp_destroy(conn->raop_rtp); conn->raop_rtp = NULL; } - } - http_response_finish(res, NULL, 0); - - logger_log(conn->raop->logger, LOGGER_DEBUG, "Handled request %s with URL %s", method, http_request_get_url(request)); + } else if (!strcmp(method, "RECORD")) { + http_response_add_header(res, "Audio-Latency", "3750"); + } else if (!strcmp(method, "GET_PARAMETER")) { + char *data = "Volume 1.000000"; + http_response_add_header(res, "Content-Type", "text/parameters"); + responseData = data; + responseDataLen = strlen(responseData); + } else { + logger_log(conn->raop->logger, LOGGER_DEBUG, "Unknown request %s with URL %s", method, http_request_get_url(request)); + } + http_response_finish(res, responseData, responseDataLen); + + logger_log(conn->raop->logger, LOGGER_DEBUG, "\nResponse:\n%s", http_response_get_data(res, &responseDataLen)); *response = res; } @@ -381,7 +462,7 @@ raop_t * raop_init(int max_clients, raop_callbacks_t *callbacks, const char *pemkey, int *error) { raop_t *raop; - httpd_t *httpd; + httpd_t *httpd,*event_httpd; rsakey_t *rsakey; httpd_callbacks_t httpd_cbs; @@ -439,6 +520,16 @@ raop_init(int max_clients, raop_callbacks_t *callbacks, const char *pemkey, int raop->httpd = httpd; raop->rsakey = rsakey; + /* Initialize the http event daemon */ + event_httpd = httpd_init(raop->logger, &httpd_cbs, max_clients); + if (!event_httpd) { + free(httpd); + rsakey_destroy(raop->rsakey); + free(raop); + return NULL; + } + raop->event_httpd = event_httpd; + return raop; } @@ -499,6 +590,10 @@ raop_set_log_callback(raop_t *raop, raop_log_callback_t callback, void *cls) int raop_start(raop_t *raop, unsigned short *port, const char *hwaddr, int hwaddrlen, const char *password) { + int ret = 0; + int i; + unsigned short eport; + assert(raop); assert(port); assert(hwaddr); @@ -523,7 +618,24 @@ raop_start(raop_t *raop, unsigned short *port, const char *hwaddr, int hwaddrlen memcpy(raop->hwaddr, hwaddr, hwaddrlen); raop->hwaddrlen = hwaddrlen; - return httpd_start(raop->httpd, port); + ret = httpd_start(raop->httpd, port); + if (ret != 1) { + logger_log(raop->logger, LOGGER_INFO, "Fail to start httpd at port %d\n", *port); + return ret; + } + + i = 10; + while (i < 100) { + eport = *port + i; + ret = httpd_start(raop->event_httpd, &eport); + if (ret == 1) break; + i += 10; + } + if (ret != 1) { + logger_log(raop->logger, LOGGER_INFO, "Fail to start event daemon at port %d\n", eport); + } + raop->event_port = eport; + return ret; } void @@ -531,6 +643,6 @@ raop_stop(raop_t *raop) { assert(raop); + httpd_stop(raop->event_httpd); httpd_stop(raop->httpd); } - diff --git a/src/lib/raop_buffer.c b/src/lib/raop_buffer.c index 8661346..f77b5a9 100644 --- a/src/lib/raop_buffer.c +++ b/src/lib/raop_buffer.c @@ -24,6 +24,8 @@ #include #include "crypto/crypto.h" #include "alac/alac.h" +#include "aac_eld/aac_eld.h" +#include "threads.h" #define RAOP_BUFFER_LENGTH 32 @@ -31,6 +33,9 @@ typedef struct { /* Packet available */ int available; + /* resend requested */ + int resending; + /* RTP header */ unsigned char flags; unsigned char type; @@ -49,9 +54,15 @@ struct raop_buffer_s { unsigned char aeskey[RAOP_AESKEY_LEN]; unsigned char aesiv[RAOP_AESIV_LEN]; + /* New AES key ctx */ + AES_KEY key_ctx; + /* ALAC decoder */ ALACSpecificConfig alacConfig; alac_file *alac; + + /* AAC-ELD decoder */ + aac_eld_file *aac_eld; /* First and last seqnum */ int is_empty; @@ -64,92 +75,121 @@ struct raop_buffer_s { /* Buffer of all audio buffers */ int buffer_size; void *buffer; + + /* encryption type*/ + int et; + + /* audio codecs*/ + int cn; + + /* mutex&conditional variable to serialize buffer accesses */ + mutex_handle_t mutex; + cond_handle_t cond; }; static int -get_fmtp_info(ALACSpecificConfig *config, const char *fmtp) +get_fmtp_info(ALACSpecificConfig *config, const char *fmtp, int cn) { - int intarr[12]; - char *original; - char *strptr; - int i; - - /* Parse fmtp string to integers */ - original = strptr = strdup(fmtp); - for (i=0; i<12; i++) { - if (strptr == NULL) { - free(original); - return -1; + if (cn == 1) { + int intarr[12]; + char *original; + char *strptr; + int i; + + /* Parse fmtp string to integers */ + original = strptr = strdup(fmtp); + for (i=0; i<12; i++) { + if (strptr == NULL) { + free(original); + return -1; + } + intarr[i] = atoi(utils_strsep(&strptr, " ")); } - intarr[i] = atoi(utils_strsep(&strptr, " ")); - } - free(original); - original = strptr = NULL; - - /* Fill the config struct */ - config->frameLength = intarr[1]; - config->compatibleVersion = intarr[2]; - config->bitDepth = intarr[3]; - config->pb = intarr[4]; - config->mb = intarr[5]; - config->kb = intarr[6]; - config->numChannels = intarr[7]; - config->maxRun = intarr[8]; - config->maxFrameBytes = intarr[9]; - config->avgBitRate = intarr[10]; - config->sampleRate = intarr[11]; - - /* Validate supported audio types */ - if (config->bitDepth != 16) { - return -2; - } - if (config->numChannels != 2) { - return -3; + free(original); + original = strptr = NULL; + + /* Fill the config struct */ + config->frameLength = intarr[1]; + config->compatibleVersion = intarr[2]; + config->bitDepth = intarr[3]; + config->pb = intarr[4]; + config->mb = intarr[5]; + config->kb = intarr[6]; + config->numChannels = intarr[7]; + config->maxRun = intarr[8]; + config->maxFrameBytes = intarr[9]; + config->avgBitRate = intarr[10]; + config->sampleRate = intarr[11]; + + /* Validate supported audio types */ + if (config->bitDepth != 16) { + return -2; + } + if (config->numChannels != 2) { + return -3; + } + } else { + /* Fill the config struct */ + config->frameLength = 4096; + config->compatibleVersion = 0; + config->bitDepth = 16; + config->pb = 40; + config->mb = 10; + config->kb = 14; + config->numChannels = 2; + config->maxRun = 255; + config->maxFrameBytes = 0; + config->avgBitRate = 0; + config->sampleRate = 44100; } return 0; } static void -set_decoder_info(alac_file *alac, ALACSpecificConfig *config) +set_decoder_info(alac_file *alac, ALACSpecificConfig *config, int cn) { - unsigned char decoder_info[48]; - memset(decoder_info, 0, sizeof(decoder_info)); + if (cn == 1) { + unsigned char decoder_info[48]; + memset(decoder_info, 0, sizeof(decoder_info)); #define SET_UINT16(buf, value)do{\ (buf)[0] = (unsigned char)((value) >> 8);\ (buf)[1] = (unsigned char)(value);\ - }while(0) +}while(0) #define SET_UINT32(buf, value)do{\ (buf)[0] = (unsigned char)((value) >> 24);\ (buf)[1] = (unsigned char)((value) >> 16);\ (buf)[2] = (unsigned char)((value) >> 8);\ (buf)[3] = (unsigned char)(value);\ - }while(0) - - /* Construct decoder info buffer */ - SET_UINT32(&decoder_info[24], config->frameLength); - decoder_info[28] = config->compatibleVersion; - decoder_info[29] = config->bitDepth; - decoder_info[30] = config->pb; - decoder_info[31] = config->mb; - decoder_info[32] = config->kb; - decoder_info[33] = config->numChannels; - SET_UINT16(&decoder_info[34], config->maxRun); - SET_UINT32(&decoder_info[36], config->maxFrameBytes); - SET_UINT32(&decoder_info[40], config->avgBitRate); - SET_UINT32(&decoder_info[44], config->sampleRate); - alac_set_info(alac, (char *) decoder_info); +}while(0) + + /* Construct decoder info buffer */ + SET_UINT32(&decoder_info[24], config->frameLength); + decoder_info[28] = config->compatibleVersion; + decoder_info[29] = config->bitDepth; + decoder_info[30] = config->pb; + decoder_info[31] = config->mb; + decoder_info[32] = config->kb; + decoder_info[33] = config->numChannels; + SET_UINT16(&decoder_info[34], config->maxRun); + SET_UINT32(&decoder_info[36], config->maxFrameBytes); + SET_UINT32(&decoder_info[40], config->avgBitRate); + SET_UINT32(&decoder_info[44], config->sampleRate); + alac_set_info(alac, (char *) decoder_info); + } else { + /* Nothing to do */ + } } raop_buffer_t * raop_buffer_init(const char *rtpmap, const char *fmtp, const unsigned char *aeskey, - const unsigned char *aesiv) + const unsigned char *aesiv, int et, int cn) { raop_buffer_t *raop_buffer; int audio_buffer_size; @@ -168,7 +208,7 @@ raop_buffer_init(const char *rtpmap, /* Parse fmtp information */ alacConfig = &raop_buffer->alacConfig; - if (get_fmtp_info(alacConfig, fmtp) < 0) { + if (get_fmtp_info(alacConfig, fmtp, cn) < 0) { free(raop_buffer); return NULL; } @@ -191,30 +231,67 @@ raop_buffer_init(const char *rtpmap, entry->audio_buffer = (char *)raop_buffer->buffer+i*audio_buffer_size; } - /* Initialize ALAC decoder */ - raop_buffer->alac = create_alac(alacConfig->bitDepth, - alacConfig->numChannels); - if (!raop_buffer->alac) { - free(raop_buffer->buffer); - free(raop_buffer); + if (cn == 1) { + /* Initialize ALAC decoder */ + raop_buffer->alac = create_alac(alacConfig->bitDepth, + alacConfig->numChannels); + if (!raop_buffer->alac) { + free(raop_buffer->buffer); + free(raop_buffer); + return NULL; + } + set_decoder_info(raop_buffer->alac, alacConfig, cn); + } else if (cn == 3) { + /* Initialize AAC-ELD decoder */ + raop_buffer->aac_eld = create_aac_eld(); + } else { + /* Todo */ return NULL; } - set_decoder_info(raop_buffer->alac, alacConfig); /* Initialize AES keys */ memcpy(raop_buffer->aeskey, aeskey, RAOP_AESKEY_LEN); memcpy(raop_buffer->aesiv, aesiv, RAOP_AESIV_LEN); + if (et != 1 && et != 3) { + /* Todo */ + fprintf(stderr, "Unknown encryption method! et=%d\n", et); + return NULL; + } + /* Mark buffer as empty */ raop_buffer->is_empty = 1; + + raop_buffer->et = et; + raop_buffer->cn = cn; + + MUTEX_CREATE(raop_buffer->mutex); + COND_CREATE(raop_buffer->cond); + return raop_buffer; } +void +raop_buffer_stop(raop_buffer_t *raop_buffer) +{ + /* make dequeue exit */ + raop_buffer->is_empty = 0; + raop_buffer->first_seqnum = 0; + raop_buffer->last_seqnum = 2; + COND_SIGNAL(raop_buffer->cond); + MUTEX_UNLOCK(raop_buffer->mutex); +} + void raop_buffer_destroy(raop_buffer_t *raop_buffer) { if (raop_buffer) { - destroy_alac(raop_buffer->alac); + if (raop_buffer->cn == 1) + destroy_alac(raop_buffer->alac); + else + destroy_aac_eld(raop_buffer->aac_eld); + MUTEX_DESTROY(raop_buffer->mutex); + COND_DESTROY(raop_buffer->cond); free(raop_buffer->buffer); free(raop_buffer); } @@ -234,6 +311,7 @@ seqnum_cmp(unsigned short s1, unsigned short s2) return (s1 - s2); } + int raop_buffer_queue(raop_buffer_t *raop_buffer, unsigned char *data, unsigned short datalen, int use_seqnum) { @@ -243,6 +321,7 @@ raop_buffer_queue(raop_buffer_t *raop_buffer, unsigned char *data, unsigned shor int encryptedlen; AES_CTX aes_ctx; int outputlen; + int i; assert(raop_buffer); @@ -251,6 +330,8 @@ raop_buffer_queue(raop_buffer_t *raop_buffer, unsigned char *data, unsigned shor return -1; } + MUTEX_LOCK(raop_buffer->mutex); + /* Get correct seqnum for the packet */ if (use_seqnum) { seqnum = (data[2] << 8) | data[3]; @@ -260,18 +341,24 @@ raop_buffer_queue(raop_buffer_t *raop_buffer, unsigned char *data, unsigned shor /* If this packet is too late, just skip it */ if (!raop_buffer->is_empty && seqnum_cmp(seqnum, raop_buffer->first_seqnum) < 0) { + MUTEX_UNLOCK(raop_buffer->mutex); return 0; } /* Check that there is always space in the buffer, otherwise flush */ if (seqnum_cmp(seqnum, raop_buffer->first_seqnum+RAOP_BUFFER_LENGTH) >= 0) { + MUTEX_UNLOCK(raop_buffer->mutex); + fprintf(stderr, "buffer overrun seqnum=%d,first=%d!\n", + seqnum,raop_buffer->first_seqnum); raop_buffer_flush(raop_buffer, seqnum); + MUTEX_LOCK(raop_buffer->mutex); } /* Get entry corresponding our seqnum */ entry = &raop_buffer->entries[seqnum % RAOP_BUFFER_LENGTH]; if (entry->available && seqnum_cmp(entry->seqnum, seqnum) == 0) { /* Packet resend, we can safely ignore */ + MUTEX_UNLOCK(raop_buffer->mutex); return 0; } @@ -284,7 +371,8 @@ raop_buffer_queue(raop_buffer_t *raop_buffer, unsigned char *data, unsigned shor entry->ssrc = (data[8] << 24) | (data[9] << 16) | (data[10] << 8) | data[11]; entry->available = 1; - + entry->resending = 0; + /* Decrypt audio data */ encryptedlen = (datalen-12)/16*16; AES_set_key(&aes_ctx, raop_buffer->aeskey, raop_buffer->aesiv, AES_MODE_128); @@ -292,9 +380,18 @@ raop_buffer_queue(raop_buffer_t *raop_buffer, unsigned char *data, unsigned shor AES_cbc_decrypt(&aes_ctx, &data[12], packetbuf, encryptedlen); memcpy(packetbuf+encryptedlen, &data[12+encryptedlen], datalen-12-encryptedlen); - /* Decode ALAC audio data */ + /* Decode audio data */ outputlen = entry->audio_buffer_size; - decode_frame(raop_buffer->alac, packetbuf, entry->audio_buffer, &outputlen); + if (raop_buffer->cn == 1) + alac_decode_frame(raop_buffer->alac, packetbuf, entry->audio_buffer, &outputlen); + else if (raop_buffer->cn == 3) + aac_eld_decode_frame(raop_buffer->aac_eld, packetbuf, datalen - 12, entry->audio_buffer, &outputlen); + else { + COND_SIGNAL(raop_buffer->cond); + MUTEX_UNLOCK(raop_buffer->mutex); + return -3; + } + entry->audio_buffer_len = outputlen; /* Update the raop_buffer seqnums */ @@ -306,6 +403,9 @@ raop_buffer_queue(raop_buffer_t *raop_buffer, unsigned char *data, unsigned shor if (seqnum_cmp(seqnum, raop_buffer->last_seqnum) > 0) { raop_buffer->last_seqnum = seqnum; } + + COND_SIGNAL(raop_buffer->cond); + MUTEX_UNLOCK(raop_buffer->mutex); return 1; } @@ -315,12 +415,14 @@ raop_buffer_dequeue(raop_buffer_t *raop_buffer, int *length, int no_resend) short buflen; raop_buffer_entry_t *entry; + MUTEX_LOCK(raop_buffer->mutex); /* Calculate number of entries in the current buffer */ buflen = seqnum_cmp(raop_buffer->last_seqnum, raop_buffer->first_seqnum)+1; /* Cannot dequeue from empty buffer */ - if (raop_buffer->is_empty || buflen <= 0) { - return NULL; + while (raop_buffer->is_empty || buflen <= 0) { + COND_WAIT(raop_buffer->cond, raop_buffer->mutex); + buflen = seqnum_cmp(raop_buffer->last_seqnum, raop_buffer->first_seqnum)+1; } /* Get the first buffer entry for inspection */ @@ -331,6 +433,7 @@ raop_buffer_dequeue(raop_buffer_t *raop_buffer, int *length, int no_resend) /* Check how much we have space left in the buffer */ if (buflen < RAOP_BUFFER_LENGTH) { /* Return nothing and hope resend gets on time */ + MUTEX_UNLOCK(raop_buffer->mutex); return NULL; } /* Risk of buffer overrun, return empty buffer */ @@ -342,13 +445,18 @@ raop_buffer_dequeue(raop_buffer_t *raop_buffer, int *length, int no_resend) /* Return an empty audio buffer to skip audio */ *length = entry->audio_buffer_size; memset(entry->audio_buffer, 0, *length); + MUTEX_UNLOCK(raop_buffer->mutex); return entry->audio_buffer; } entry->available = 0; + entry->resending = 0; /* Return entry audio buffer */ *length = entry->audio_buffer_len; entry->audio_buffer_len = 0; + + MUTEX_UNLOCK(raop_buffer->mutex); + return entry->audio_buffer; } @@ -360,21 +468,25 @@ raop_buffer_handle_resends(raop_buffer_t *raop_buffer, raop_resend_cb_t resend_c assert(raop_buffer); assert(resend_cb); + MUTEX_LOCK(raop_buffer->mutex); if (seqnum_cmp(raop_buffer->first_seqnum, raop_buffer->last_seqnum) < 0) { int seqnum, count; for (seqnum=raop_buffer->first_seqnum; seqnum_cmp(seqnum, raop_buffer->last_seqnum)<0; seqnum++) { entry = &raop_buffer->entries[seqnum % RAOP_BUFFER_LENGTH]; - if (entry->available) { + if (entry->available || entry->resending) { break; } + entry->resending = 1; } if (seqnum_cmp(seqnum, raop_buffer->first_seqnum) == 0) { + MUTEX_UNLOCK(raop_buffer->mutex); return; } count = seqnum_cmp(seqnum, raop_buffer->first_seqnum); resend_cb(opaque, raop_buffer->first_seqnum, count); } + MUTEX_UNLOCK(raop_buffer->mutex); } void @@ -384,8 +496,10 @@ raop_buffer_flush(raop_buffer_t *raop_buffer, int next_seq) assert(raop_buffer); + MUTEX_LOCK(raop_buffer->mutex); for (i=0; ientries[i].available = 0; + raop_buffer->entries[i].resending = 0; raop_buffer->entries[i].audio_buffer_len = 0; } if (next_seq < 0 || next_seq > 0xffff) { @@ -394,4 +508,6 @@ raop_buffer_flush(raop_buffer_t *raop_buffer, int next_seq) raop_buffer->first_seqnum = next_seq; raop_buffer->last_seqnum = next_seq-1; } + COND_SIGNAL(raop_buffer->cond); + MUTEX_UNLOCK(raop_buffer->mutex); } diff --git a/src/lib/raop_buffer.h b/src/lib/raop_buffer.h index bed0ef4..ee442e9 100644 --- a/src/lib/raop_buffer.h +++ b/src/lib/raop_buffer.h @@ -37,7 +37,7 @@ typedef int (*raop_resend_cb_t)(void *opaque, unsigned short seqno, unsigned sho raop_buffer_t *raop_buffer_init(const char *rtpmap, const char *fmtp, const unsigned char *aeskey, - const unsigned char *aesiv); + const unsigned char *aesiv, int et, int cn); const ALACSpecificConfig *raop_buffer_get_config(raop_buffer_t *raop_buffer); int raop_buffer_queue(raop_buffer_t *raop_buffer, unsigned char *data, unsigned short datalen, int use_seqnum); @@ -46,5 +46,6 @@ void raop_buffer_handle_resends(raop_buffer_t *raop_buffer, raop_resend_cb_t res void raop_buffer_flush(raop_buffer_t *raop_buffer, int next_seq); void raop_buffer_destroy(raop_buffer_t *raop_buffer); +void raop_buffer_stop(raop_buffer_t *raop_buffer); #endif diff --git a/src/lib/raop_rtp.c b/src/lib/raop_rtp.c index e80da78..a1086a0 100644 --- a/src/lib/raop_rtp.c +++ b/src/lib/raop_rtp.c @@ -52,7 +52,8 @@ struct raop_rtp_s { int coverart_len; int flush; - thread_handle_t thread; + thread_handle_t thread_rtp; + thread_handle_t thread_audio; mutex_handle_t run_mutex; /* MUTEX LOCKED VARIABLES END */ @@ -122,7 +123,7 @@ raop_rtp_parse_remote(raop_rtp_t *raop_rtp, const char *remote) raop_rtp_t * raop_rtp_init(logger_t *logger, raop_callbacks_t *callbacks, const char *remote, const char *rtpmap, const char *fmtp, - const unsigned char *aeskey, const unsigned char *aesiv) + const unsigned char *aeskey, const unsigned char *aesiv, int et, int cn) { raop_rtp_t *raop_rtp; @@ -138,7 +139,7 @@ raop_rtp_init(logger_t *logger, raop_callbacks_t *callbacks, const char *remote, } raop_rtp->logger = logger; memcpy(&raop_rtp->callbacks, callbacks, sizeof(raop_callbacks_t)); - raop_rtp->buffer = raop_buffer_init(rtpmap, fmtp, aeskey, aesiv); + raop_rtp->buffer = raop_buffer_init(rtpmap, fmtp, aeskey, aesiv, et, cn); if (!raop_rtp->buffer) { free(raop_rtp); return NULL; @@ -321,16 +322,16 @@ raop_rtp_process_events(raop_rtp_t *raop_rtp, void *cb_data) } static THREAD_RETVAL -raop_rtp_thread_udp(void *arg) +raop_rtp_audio_thread(void *arg) { raop_rtp_t *raop_rtp = arg; - unsigned char packet[RAOP_PACKET_LEN]; - unsigned int packetlen; - struct sockaddr_storage saddr; - socklen_t saddrlen; const ALACSpecificConfig *config; void *cb_data = NULL; + int no_resend = (raop_rtp->control_rport == 0); + + const void *audiobuf; + int audiobuflen; assert(raop_rtp); @@ -339,14 +340,43 @@ raop_rtp_thread_udp(void *arg) config->bitDepth, config->numChannels, config->sampleRate); + + while (1) { + /* Check if we are still running and process callbacks */ + if (raop_rtp_process_events(raop_rtp, cb_data)) { + logger_log(raop_rtp->logger, LOGGER_DEBUG, "rtp not running"); + break; + } + + /* play a frame in queue */ + if ((audiobuf = raop_buffer_dequeue(raop_rtp->buffer, &audiobuflen, no_resend))) { + raop_rtp->callbacks.audio_process(raop_rtp->callbacks.cls, cb_data, audiobuf, audiobuflen); + } + + } + + raop_rtp->callbacks.audio_destroy(raop_rtp->callbacks.cls, cb_data); + logger_log(raop_rtp->logger, LOGGER_INFO, "Exiting RAOP audio thread"); +} + +static THREAD_RETVAL +raop_rtp_thread_udp(void *arg) +{ + raop_rtp_t *raop_rtp = arg; + unsigned char packet[RAOP_PACKET_LEN]; + unsigned int packetlen; + struct sockaddr_storage saddr; + socklen_t saddrlen; + + assert(raop_rtp); while(1) { fd_set rfds; struct timeval tv; int nfds, ret; - /* Check if we are still running and process callbacks */ - if (raop_rtp_process_events(raop_rtp, cb_data)) { + if (!raop_rtp->running) { + MUTEX_UNLOCK(raop_rtp->run_mutex); break; } @@ -372,6 +402,7 @@ raop_rtp_thread_udp(void *arg) continue; } else if (ret == -1) { /* FIXME: Error happened */ + logger_log(raop_rtp->logger, LOGGER_DEBUG, "select error"); break; } @@ -404,26 +435,18 @@ raop_rtp_thread_udp(void *arg) int no_resend = (raop_rtp->control_rport == 0); int ret; - const void *audiobuf; - int audiobuflen; ret = raop_buffer_queue(raop_rtp->buffer, packet, packetlen, 1); assert(ret >= 0); - /* Decode all frames in queue */ - while ((audiobuf = raop_buffer_dequeue(raop_rtp->buffer, &audiobuflen, no_resend))) { - raop_rtp->callbacks.audio_process(raop_rtp->callbacks.cls, cb_data, audiobuf, audiobuflen); - } - /* Handle possible resend requests */ if (!no_resend) { raop_buffer_handle_resends(raop_rtp->buffer, raop_rtp_resend_callback, raop_rtp); } } - } + } } logger_log(raop_rtp->logger, LOGGER_INFO, "Exiting UDP RAOP thread"); - raop_rtp->callbacks.audio_destroy(raop_rtp->callbacks.cls, cb_data); return 0; } @@ -436,24 +459,16 @@ raop_rtp_thread_tcp(void *arg) unsigned char packet[RAOP_PACKET_LEN]; unsigned int packetlen = 0; - const ALACSpecificConfig *config; - void *cb_data = NULL; - assert(raop_rtp); - config = raop_buffer_get_config(raop_rtp->buffer); - cb_data = raop_rtp->callbacks.audio_init(raop_rtp->callbacks.cls, - config->bitDepth, - config->numChannels, - config->sampleRate); - while (1) { fd_set rfds; struct timeval tv; int nfds, ret; - /* Check if we are still running and process callbacks */ - if (raop_rtp_process_events(raop_rtp, cb_data)) { + MUTEX_LOCK(raop_rtp->run_mutex); + if (!raop_rtp->running) { + MUTEX_UNLOCK(raop_rtp->run_mutex); break; } @@ -536,10 +551,6 @@ raop_rtp_thread_tcp(void *arg) memmove(packet, packet+4+rtplen, packetlen-rtplen); packetlen -= 4+rtplen; - /* Decode the received frame */ - if ((audiobuf = raop_buffer_dequeue(raop_rtp->buffer, &audiobuflen, 1))) { - raop_rtp->callbacks.audio_process(raop_rtp->callbacks.cls, cb_data, audiobuf, audiobuflen); - } } } @@ -549,7 +560,6 @@ raop_rtp_thread_tcp(void *arg) } logger_log(raop_rtp->logger, LOGGER_INFO, "Exiting TCP RAOP thread"); - raop_rtp->callbacks.audio_destroy(raop_rtp->callbacks.cls, cb_data); return 0; } @@ -587,10 +597,11 @@ raop_rtp_start(raop_rtp_t *raop_rtp, int use_udp, unsigned short control_rport, raop_rtp->running = 1; raop_rtp->joined = 0; if (use_udp) { - THREAD_CREATE(raop_rtp->thread, raop_rtp_thread_udp, raop_rtp); + THREAD_CREATE(raop_rtp->thread_rtp, raop_rtp_thread_udp, raop_rtp); } else { - THREAD_CREATE(raop_rtp->thread, raop_rtp_thread_tcp, raop_rtp); + THREAD_CREATE(raop_rtp->thread_rtp, raop_rtp_thread_tcp, raop_rtp); } + THREAD_CREATE(raop_rtp->thread_audio, raop_rtp_audio_thread, raop_rtp); MUTEX_UNLOCK(raop_rtp->run_mutex); } @@ -680,14 +691,18 @@ raop_rtp_stop(raop_rtp_t *raop_rtp) raop_rtp->running = 0; MUTEX_UNLOCK(raop_rtp->run_mutex); + raop_buffer_stop(raop_rtp->buffer); + /* Join the thread */ - THREAD_JOIN(raop_rtp->thread); + THREAD_JOIN(raop_rtp->thread_rtp); + THREAD_JOIN(raop_rtp->thread_audio); if (raop_rtp->csock != -1) closesocket(raop_rtp->csock); if (raop_rtp->tsock != -1) closesocket(raop_rtp->tsock); if (raop_rtp->dsock != -1) closesocket(raop_rtp->dsock); /* Flush buffer into initial state */ - raop_buffer_flush(raop_rtp->buffer, -1); + /* with mutex this might deadlock */ + //raop_buffer_flush(raop_rtp->buffer, -1); /* Mark thread as joined */ MUTEX_LOCK(raop_rtp->run_mutex); diff --git a/src/lib/raop_rtp.h b/src/lib/raop_rtp.h index 51cd891..01bc5bd 100644 --- a/src/lib/raop_rtp.h +++ b/src/lib/raop_rtp.h @@ -27,7 +27,7 @@ typedef struct raop_rtp_s raop_rtp_t; raop_rtp_t *raop_rtp_init(logger_t *logger, raop_callbacks_t *callbacks, const char *remote, const char *rtpmap, const char *fmtp, - const unsigned char *aeskey, const unsigned char *aesiv); + const unsigned char *aeskey, const unsigned char *aesiv, int et, int cn); void raop_rtp_start(raop_rtp_t *raop_rtp, int use_udp, unsigned short control_rport, unsigned short timing_rport, unsigned short *control_lport, unsigned short *timing_lport, unsigned short *data_lport); void raop_rtp_set_volume(raop_rtp_t *raop_rtp, float volume); diff --git a/src/lib/rsakey.c b/src/lib/rsakey.c index e28b02c..f464b12 100644 --- a/src/lib/rsakey.c +++ b/src/lib/rsakey.c @@ -290,6 +290,20 @@ rsakey_mfg1(unsigned char *dst, int dstlen, const unsigned char *seed, int seedl return masklen; } +int +rsakey_base64_decode(rsakey_t *rsakey, unsigned char **output, const char *b64input) +{ + int outlen; + + assert(rsakey); + if (!output || !b64input) { + return -1; + } + + outlen = base64_decode(rsakey->base64, output, b64input, strlen(b64input)); + return outlen; +} + /* OAEP decryption with SHA-1 hash */ /* See RFC 3447 7.1.2 for more information */ int diff --git a/src/lib/rsakey.h b/src/lib/rsakey.h index f636108..0c251d6 100644 --- a/src/lib/rsakey.h +++ b/src/lib/rsakey.h @@ -31,6 +31,7 @@ int rsakey_sign(rsakey_t *rsakey, char *dst, int dstlen, const char *b64digest, unsigned char *ipaddr, int ipaddrlen, unsigned char *hwaddr, int hwaddrlen); +int rsakey_base64_decode(rsakey_t *rsakey, unsigned char **output, const char *b64input); int rsakey_decrypt(rsakey_t *rsakey, unsigned char *dst, int dstlen, const char *b64input); int rsakey_parseiv(rsakey_t *rsakey, unsigned char *dst, int dstlen, const char *b64input); diff --git a/src/lib/sdp.c b/src/lib/sdp.c index b39661c..181645e 100644 --- a/src/lib/sdp.c +++ b/src/lib/sdp.c @@ -33,6 +33,7 @@ struct sdp_s { const char *rtpmap; const char *fmtp; const char *rsaaeskey; + const char *fpaeskey; const char *aesiv; const char *min_latency; }; @@ -81,6 +82,8 @@ parse_sdp_line(sdp_t *sdp, char *line) sdp->fmtp = value; } else if (!strcmp(key, "rsaaeskey")) { sdp->rsaaeskey = value; + } else if (!strcmp(key, "fpaeskey")) { + sdp->fpaeskey = value; } else if (!strcmp(key, "aesiv")) { sdp->aesiv = value; } else if (!strcmp(key, "min-latency")) { @@ -224,6 +227,14 @@ sdp_get_rsaaeskey(sdp_t *sdp) return sdp->rsaaeskey; } +const char * +sdp_get_fpaeskey(sdp_t *sdp) +{ + assert(sdp); + + return sdp->fpaeskey; +} + const char * sdp_get_aesiv(sdp_t *sdp) { diff --git a/src/lib/sdp.h b/src/lib/sdp.h index 708cbf4..79048b3 100644 --- a/src/lib/sdp.h +++ b/src/lib/sdp.h @@ -28,6 +28,7 @@ const char *sdp_get_media(sdp_t *sdp); const char *sdp_get_rtpmap(sdp_t *sdp); const char *sdp_get_fmtp(sdp_t *sdp); const char *sdp_get_rsaaeskey(sdp_t *sdp); +const char *sdp_get_fpaeskey(sdp_t *sdp); const char *sdp_get_aesiv(sdp_t *sdp); const char *sdp_get_min_latency(sdp_t *sdp); diff --git a/src/lib/threads.h b/src/lib/threads.h index 70dd416..bdc22d9 100644 --- a/src/lib/threads.h +++ b/src/lib/threads.h @@ -34,6 +34,8 @@ typedef HANDLE mutex_handle_t; #define MUTEX_UNLOCK(handle) ReleaseMutex(handle) #define MUTEX_DESTROY(handle) CloseHandle(handle) +/* TODO: cancel & conditional variable support */ + #else /* Use pthread library */ #include @@ -47,14 +49,21 @@ typedef pthread_t thread_handle_t; #define THREAD_CREATE(handle, func, arg) \ if (pthread_create(&(handle), NULL, func, arg)) handle = 0 #define THREAD_JOIN(handle) pthread_join(handle, NULL) +#define THREAD_CANCEL(handle) pthread_cancel(handle) typedef pthread_mutex_t mutex_handle_t; +typedef pthread_cond_t cond_handle_t; #define MUTEX_CREATE(handle) pthread_mutex_init(&(handle), NULL) #define MUTEX_LOCK(handle) pthread_mutex_lock(&(handle)) #define MUTEX_UNLOCK(handle) pthread_mutex_unlock(&(handle)) #define MUTEX_DESTROY(handle) pthread_mutex_destroy(&(handle)) +#define COND_CREATE(handle) pthread_cond_init(&(handle), NULL) +#define COND_WAIT(handle, mutex) pthread_cond_wait(&(handle), &(mutex)) +#define COND_SIGNAL(handle) pthread_cond_signal(&(handle)) +#define COND_DESTROY(handle) pthread_cond_destroy(&(handle)) + #endif #endif /* THREADS_H */ diff --git a/src/shairplay.c b/src/shairplay.c index 4b873f1..9b406c2 100644 --- a/src/shairplay.c +++ b/src/shairplay.c @@ -34,33 +34,25 @@ #include #include +#include -#include +#include "audio.h" #include "config.h" typedef struct { char apname[56]; char password[56]; - unsigned short port; + unsigned short port_raop; + unsigned short port_airplay; char hwaddr[6]; char ao_driver[56]; char ao_devicename[56]; char ao_deviceid[16]; + int enable_airplay; } shairplay_options_t; -typedef struct { - ao_device *device; - - int buffering; - int buflen; - char buffer[8192]; - - float volume; -} shairplay_session_t; - - static int running; #ifndef WIN32 @@ -118,146 +110,18 @@ parse_hwaddr(const char *str, char *hwaddr, int hwaddrlen) return 0; } -static ao_device * -audio_open_device(shairplay_options_t *opt, int bits, int channels, int samplerate) -{ - ao_device *device = NULL; - ao_option *ao_options = NULL; - ao_sample_format format; - int driver_id; - - /* Get the libao driver ID */ - if (strlen(opt->ao_driver)) { - driver_id = ao_driver_id(opt->ao_driver); - } else { - driver_id = ao_default_driver_id(); - } - - /* Add all available libao options */ - if (strlen(opt->ao_devicename)) { - ao_append_option(&ao_options, "dev", opt->ao_devicename); - } - if (strlen(opt->ao_deviceid)) { - ao_append_option(&ao_options, "id", opt->ao_deviceid); - } - - /* Set audio format */ - memset(&format, 0, sizeof(format)); - format.bits = bits; - format.channels = channels; - format.rate = samplerate; - format.byte_format = AO_FMT_NATIVE; - - /* Try opening the actual device */ - device = ao_open_live(driver_id, &format, ao_options); - ao_free_options(ao_options); - return device; -} - -static void * -audio_init(void *cls, int bits, int channels, int samplerate) -{ - shairplay_options_t *options = cls; - shairplay_session_t *session; - - session = calloc(1, sizeof(shairplay_session_t)); - assert(session); - - session->device = audio_open_device(options, bits, channels, samplerate); - if (session->device == NULL) { - printf("Error opening device %d\n", errno); - } - assert(session->device); - - session->buffering = 1; - session->volume = 1.0f; - return session; -} - -static int -audio_output(shairplay_session_t *session, const void *buffer, int buflen) -{ - short *shortbuf; - char tmpbuf[4096]; - int tmpbuflen, i; - - tmpbuflen = (buflen > sizeof(tmpbuf)) ? sizeof(tmpbuf) : buflen; - memcpy(tmpbuf, buffer, tmpbuflen); - if (ao_is_big_endian()) { - for (i=0; ivolume; - } - ao_play(session->device, tmpbuf, tmpbuflen); - return tmpbuflen; -} - -static void -audio_process(void *cls, void *opaque, const void *buffer, int buflen) -{ - shairplay_session_t *session = opaque; - int processed; - - if (session->buffering) { - printf("Buffering...\n"); - if (session->buflen+buflen < sizeof(session->buffer)) { - memcpy(session->buffer+session->buflen, buffer, buflen); - session->buflen += buflen; - return; - } - session->buffering = 0; - printf("Finished buffering...\n"); - - processed = 0; - while (processed < session->buflen) { - processed += audio_output(session, - session->buffer+processed, - session->buflen-processed); - } - session->buflen = 0; - } - - processed = 0; - while (processed < buflen) { - processed += audio_output(session, - buffer+processed, - buflen-processed); - } -} - -static void -audio_destroy(void *cls, void *opaque) -{ - shairplay_session_t *session = opaque; - - ao_close(session->device); - free(session); -} - -static void -audio_set_volume(void *cls, void *opaque, float volume) -{ - shairplay_session_t *session = opaque; - session->volume = pow(10.0, 0.05*volume); -} - static int parse_options(shairplay_options_t *opt, int argc, char *argv[]) { - const char default_hwaddr[] = { 0x48, 0x5d, 0x60, 0x7c, 0xee, 0x22 }; + const char default_hwaddr[] = { 0x00, 0x24, 0xd7, 0xb2, 0x2e, 0x60 }; char *path = argv[0]; char *arg; /* Set default values for apname and port */ strncpy(opt->apname, "Shairplay", sizeof(opt->apname)-1); - opt->port = 5000; + opt->port_raop = 5000; + opt->port_airplay = 7000; memcpy(opt->hwaddr, default_hwaddr, sizeof(opt->hwaddr)); while ((arg = *++argv)) { @@ -270,9 +134,10 @@ parse_options(shairplay_options_t *opt, int argc, char *argv[]) } else if (!strncmp(arg, "--password=", 11)) { strncpy(opt->password, arg+11, sizeof(opt->password)-1); } else if (!strcmp(arg, "-o")) { - opt->port = atoi(*++argv); + opt->port_raop = atoi(*++argv); } else if (!strncmp(arg, "--server_port=", 14)) { - opt->port = atoi(arg+14); + opt->port_raop = atoi(arg+14); + opt->port_airplay = atoi(arg+14) + 2000; } else if (!strncmp(arg, "--hwaddr=", 9)) { if (parse_hwaddr(arg+9, opt->hwaddr, sizeof(opt->hwaddr))) { fprintf(stderr, "Invalid format given for hwaddr, aborting...\n"); @@ -285,17 +150,20 @@ parse_options(shairplay_options_t *opt, int argc, char *argv[]) strncpy(opt->ao_devicename, arg+16, sizeof(opt->ao_devicename)-1); } else if (!strncmp(arg, "--ao_deviceid=", 14)) { strncpy(opt->ao_deviceid, arg+14, sizeof(opt->ao_deviceid)-1); + } else if (!strncmp(arg, "--enable_airplay", 16)) { + opt->enable_airplay = 1; } else if (!strcmp(arg, "-h") || !strcmp(arg, "--help")) { fprintf(stderr, "Shairplay version %s\n", VERSION); fprintf(stderr, "Usage: %s [OPTION...]\n", path); fprintf(stderr, "\n"); fprintf(stderr, " -a, --apname=AirPort Sets Airport name\n"); fprintf(stderr, " -p, --password=secret Sets password\n"); - fprintf(stderr, " -o, --server_port=5000 Sets port for RAOP service\n"); + fprintf(stderr, " -o, --server_port=5000 Sets port for RAOP service, port+2000 for AIRPLAY service\n"); fprintf(stderr, " --hwaddr=address Sets the MAC address, useful if running multiple instances\n"); fprintf(stderr, " --ao_driver=driver Sets the ao driver (optional)\n"); fprintf(stderr, " --ao_devicename=devicename Sets the ao device name (optional)\n"); fprintf(stderr, " --ao_deviceid=id Sets the ao device id (optional)\n"); + fprintf(stderr, " --enable_airplay start airplay service\n"); fprintf(stderr, " -h, --help This help\n"); fprintf(stderr, "\n"); return 1; @@ -314,6 +182,8 @@ main(int argc, char *argv[]) dnssd_t *dnssd; raop_t *raop; raop_callbacks_t raop_cbs; + airplay_t *airplay; + airplay_callbacks_t airplay_cbs; char *password = NULL; int error; @@ -327,24 +197,8 @@ main(int argc, char *argv[]) return 0; } - ao_initialize(); - - device = audio_open_device(&options, 16, 2, 44100); - if (device == NULL) { - fprintf(stderr, "Error opening audio device %d\n", errno); - fprintf(stderr, "Please check your libao settings and try again\n"); + if (audio_prepare(options.ao_driver, options.ao_devicename, options.ao_deviceid, &raop_cbs) < 0) return -1; - } else { - ao_close(device); - device = NULL; - } - - memset(&raop_cbs, 0, sizeof(raop_cbs)); - raop_cbs.cls = &options; - raop_cbs.audio_init = audio_init; - raop_cbs.audio_process = audio_process; - raop_cbs.audio_destroy = audio_destroy; - raop_cbs.audio_set_volume = audio_set_volume; raop = raop_init_from_keyfile(10, &raop_cbs, "airport.key", NULL); if (raop == NULL) { @@ -357,7 +211,29 @@ main(int argc, char *argv[]) password = options.password; } raop_set_log_level(raop, RAOP_LOG_DEBUG); - raop_start(raop, &options.port, options.hwaddr, sizeof(options.hwaddr), password); + raop_start(raop, &options.port_raop, options.hwaddr, sizeof(options.hwaddr), password); + + if (options.enable_airplay) { + /* TODO: fix the callbacks */ + memset(&airplay_cbs, 0, sizeof(airplay_cbs)); +#if 0 + airplay_cbs.cls = &options; + airplay_cbs.audio_init = audio_init; + airplay_cbs.audio_process = audio_process; + airplay_cbs.audio_destroy = audio_destroy; + airplay_cbs.audio_set_volume = audio_set_volume; +#endif + + airplay = airplay_init_from_keyfile(10, &airplay_cbs, "airport.key", NULL); + if (airplay == NULL) { + fprintf(stderr, "Could not initialize the AIRPLAY service\n"); + fprintf(stderr, "Please make sure the airport.key file is in the current directory.\n"); + return -1; + } + + airplay_set_log_level(airplay, AIRPLAY_LOG_DEBUG); + airplay_start(airplay, &options.port_airplay, options.hwaddr, sizeof(options.hwaddr), password); + } error = 0; dnssd = dnssd_init(&error); @@ -368,10 +244,13 @@ main(int argc, char *argv[]) fprintf(stderr, "Windows: Try installing http://support.apple.com/kb/DL999\n"); fprintf(stderr, "Debian/Ubuntu: Try installing libavahi-compat-libdnssd-dev package\n"); raop_destroy(raop); + airplay_destroy(airplay); return -1; } - dnssd_register_raop(dnssd, options.apname, options.port, options.hwaddr, sizeof(options.hwaddr), 0); + dnssd_register_raop(dnssd, options.apname, options.port_raop, options.hwaddr, sizeof(options.hwaddr), 0); + if (options.enable_airplay) + dnssd_register_airplay(dnssd, options.apname, options.port_airplay, options.hwaddr, sizeof(options.hwaddr)); running = 1; while (running) { @@ -383,12 +262,18 @@ main(int argc, char *argv[]) } dnssd_unregister_raop(dnssd); + if (options.enable_airplay) dnssd_unregister_airplay(dnssd); dnssd_destroy(dnssd); raop_stop(raop); raop_destroy(raop); - ao_shutdown(); + audio_shutdown(); + + if (options.enable_airplay) { + airplay_stop(airplay); + airplay_destroy(airplay); + } return 0; }