1127 lines
32 KiB
C
1127 lines
32 KiB
C
/*
|
|
* ShairPort Initializer - Network Initializer/Mgr for Hairtunes RAOP
|
|
* Copyright (c) M. Andrew Webster 2011
|
|
* All rights reserved.
|
|
*
|
|
* 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 <fcntl.h>
|
|
#include "socketlib.h"
|
|
#include "shairport.h"
|
|
#include "hairtunes.h"
|
|
|
|
#ifndef TRUE
|
|
#define TRUE (-1)
|
|
#endif
|
|
#ifndef FALSE
|
|
#define FALSE (0)
|
|
#endif
|
|
|
|
// TEMP
|
|
|
|
int kCurrentLogLevel = LOG_INFO;
|
|
extern int buffer_start_fill;
|
|
|
|
#ifdef _WIN32
|
|
#define DEVNULL "nul"
|
|
#else
|
|
#define DEVNULL "/dev/null"
|
|
#endif
|
|
#define RSA_LOG_LEVEL LOG_DEBUG_VV
|
|
#define SOCKET_LOG_LEVEL LOG_DEBUG_VV
|
|
#define HEADER_LOG_LEVEL LOG_DEBUG
|
|
#define AVAHI_LOG_LEVEL LOG_DEBUG
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
char tHWID[HWID_SIZE] = {0,51,52,53,54,55};
|
|
char tHWID_Hex[HWID_SIZE * 2 + 1];
|
|
memset(tHWID_Hex, 0, sizeof(tHWID_Hex));
|
|
|
|
char tServerName[56] = "ShairPort";
|
|
char tPassword[56] = "";
|
|
|
|
struct addrinfo *tAddrInfo;
|
|
int tSimLevel = 0;
|
|
int tUseKnownHWID = FALSE;
|
|
int tDaemonize = FALSE;
|
|
int tPort = PORT;
|
|
|
|
char *arg;
|
|
while ( (arg = *++argv) ) {
|
|
if(!strcmp(arg, "-a"))
|
|
{
|
|
strncpy(tServerName, *++argv, 55);
|
|
argc--;
|
|
}
|
|
else if(!strncmp(arg, "--apname=", 9))
|
|
{
|
|
strncpy(tServerName, arg+9, 55);
|
|
}
|
|
else if(!strcmp(arg, "-p"))
|
|
{
|
|
strncpy(tPassword, *++argv, 55);
|
|
argc--;
|
|
}
|
|
else if(!strncmp(arg, "--password=",11 ))
|
|
{
|
|
strncpy(tPassword, arg+11, 55);
|
|
}
|
|
else if(!strcmp(arg, "-o"))
|
|
{
|
|
tPort = atoi(*++argv);
|
|
argc--;
|
|
}
|
|
else if(!strncmp(arg, "--server_port=", 14))
|
|
{
|
|
tPort = atoi(arg+14);
|
|
}
|
|
else if(!strcmp(arg, "-b"))
|
|
{
|
|
buffer_start_fill = atoi(*++argv);
|
|
argc--;
|
|
}
|
|
else if(!strncmp(arg, "--buffer=", 9))
|
|
{
|
|
buffer_start_fill = atoi(arg + 9);
|
|
}
|
|
else if(!strcmp(arg, "-k"))
|
|
{
|
|
tUseKnownHWID = TRUE;
|
|
}
|
|
else if(!strcmp(arg, "-q") || !strncmp(arg, "--quiet", 7))
|
|
{
|
|
kCurrentLogLevel = 0;
|
|
}
|
|
else if(!strcmp(arg, "-d"))
|
|
{
|
|
tDaemonize = TRUE;
|
|
kCurrentLogLevel = 0;
|
|
}
|
|
else if(!strcmp(arg, "-v"))
|
|
{
|
|
kCurrentLogLevel = LOG_DEBUG;
|
|
}
|
|
else if(!strcmp(arg, "-v2"))
|
|
{
|
|
kCurrentLogLevel = LOG_DEBUG_V;
|
|
}
|
|
else if(!strcmp(arg, "-vv") || !strcmp(arg, "-v3"))
|
|
{
|
|
kCurrentLogLevel = LOG_DEBUG_VV;
|
|
}
|
|
else if(!strcmp(arg, "-h") || !strcmp(arg, "--help"))
|
|
{
|
|
slog(LOG_INFO, "ShairPort version 0.05 C port - Airport Express emulator\n");
|
|
slog(LOG_INFO, "Usage:\nshairport [OPTION...]\n\nOptions:\n");
|
|
slog(LOG_INFO, " -a, --apname=AirPort Sets Airport name\n");
|
|
slog(LOG_INFO, " -p, --password=secret Sets Password (not working)\n");
|
|
slog(LOG_INFO, " -o, --server_port=5000 Sets Port for Avahi/dns-sd\n");
|
|
slog(LOG_INFO, " -b, --buffer=282 Sets Number of frames to buffer before beginning playback\n");
|
|
slog(LOG_INFO, " -d Daemon mode\n");
|
|
slog(LOG_INFO, " -q, --quiet Supresses all output.\n");
|
|
slog(LOG_INFO, " -v,-v2,-v3,-vv Various debugging levels\n");
|
|
slog(LOG_INFO, "\n");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if ( buffer_start_fill < 30 || buffer_start_fill > BUFFER_FRAMES ) {
|
|
fprintf(stderr, "buffer value must be > 30 and < %d\n", BUFFER_FRAMES);
|
|
return(0);
|
|
}
|
|
|
|
if(tDaemonize)
|
|
{
|
|
int tPid = fork();
|
|
if(tPid < 0)
|
|
{
|
|
exit(1); // Error on fork
|
|
}
|
|
else if(tPid > 0)
|
|
{
|
|
exit(0);
|
|
}
|
|
else
|
|
{
|
|
setsid();
|
|
int tIdx = 0;
|
|
for(tIdx = getdtablesize(); tIdx >= 0; --tIdx)
|
|
{
|
|
close(tIdx);
|
|
}
|
|
tIdx = open(DEVNULL, O_RDWR);
|
|
dup(tIdx);
|
|
dup(tIdx);
|
|
}
|
|
}
|
|
srand ( time(NULL) );
|
|
// Copy over empty 00's
|
|
//tPrintHWID[tIdx] = tAddr[0];
|
|
|
|
int tIdx = 0;
|
|
for(tIdx=0;tIdx<HWID_SIZE;tIdx++)
|
|
{
|
|
if(tIdx > 0)
|
|
{
|
|
if(!tUseKnownHWID)
|
|
{
|
|
int tVal = ((random() % 80) + 33);
|
|
tHWID[tIdx] = tVal;
|
|
}
|
|
//tPrintHWID[tIdx] = tAddr[tIdx];
|
|
}
|
|
sprintf(tHWID_Hex+(tIdx*2), "%02X",tHWID[tIdx]);
|
|
}
|
|
//tPrintHWID[HWID_SIZE] = '\0';
|
|
|
|
slog(LOG_INFO, "LogLevel: %d\n", kCurrentLogLevel);
|
|
slog(LOG_INFO, "AirName: %s\n", tServerName);
|
|
slog(LOG_INFO, "HWID: %.*s\n", HWID_SIZE, tHWID+1);
|
|
slog(LOG_INFO, "HWID_Hex(%d): %s\n", strlen(tHWID_Hex), tHWID_Hex);
|
|
|
|
if(tSimLevel >= 1)
|
|
{
|
|
#ifdef SIM_INCL
|
|
sim(tSimLevel, tTestValue, tHWID);
|
|
#endif
|
|
return(1);
|
|
}
|
|
else
|
|
{
|
|
startAvahi(tHWID_Hex, tServerName, tPort);
|
|
slog(LOG_DEBUG_V, "Starting connection server: specified server port: %d\n", tPort);
|
|
int tServerSock = setupListenServer(&tAddrInfo, tPort);
|
|
if(tServerSock < 0)
|
|
{
|
|
freeaddrinfo(tAddrInfo);
|
|
slog(LOG_INFO, "Error setting up server socket on port %d, try specifying a different port\n", tPort);
|
|
exit(1);
|
|
}
|
|
|
|
int tClientSock = 0;
|
|
while(1)
|
|
{
|
|
slog(LOG_DEBUG_V, "Waiting for clients to connect\n");
|
|
tClientSock = acceptClient(tServerSock, tAddrInfo);
|
|
if(tClientSock > 0)
|
|
{
|
|
int tPid = fork();
|
|
if(tPid == 0)
|
|
{
|
|
freeaddrinfo(tAddrInfo);
|
|
tAddrInfo = NULL;
|
|
slog(LOG_DEBUG, "...Accepted Client Connection..\n");
|
|
close(tServerSock);
|
|
handleClient(tClientSock, tPassword, tHWID);
|
|
//close(tClientSock);
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
slog(LOG_DEBUG_VV, "Child now busy handling new client\n");
|
|
close(tClientSock);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// failed to init server socket....try waiting a moment...
|
|
sleep(2);
|
|
}
|
|
}
|
|
}
|
|
|
|
slog(LOG_DEBUG_VV, "Finished, and waiting to clean everything up\n");
|
|
sleep(1);
|
|
if(tAddrInfo != NULL)
|
|
{
|
|
freeaddrinfo(tAddrInfo);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int findEnd(char *tReadBuf)
|
|
{
|
|
// find \n\n, \r\n\r\n, or \r\r is found
|
|
int tIdx = 0;
|
|
int tLen = strlen(tReadBuf);
|
|
for(tIdx = 0; tIdx < tLen; tIdx++)
|
|
{
|
|
if(tReadBuf[tIdx] == '\r')
|
|
{
|
|
if(tIdx + 1 < tLen)
|
|
{
|
|
if(tReadBuf[tIdx+1] == '\r')
|
|
{
|
|
return (tIdx+1);
|
|
}
|
|
else if(tIdx+3 < tLen)
|
|
{
|
|
if(tReadBuf[tIdx+1] == '\n' &&
|
|
tReadBuf[tIdx+2] == '\r' &&
|
|
tReadBuf[tIdx+3] == '\n')
|
|
{
|
|
return (tIdx+3);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(tReadBuf[tIdx] == '\n')
|
|
{
|
|
if(tIdx + 1 < tLen && tReadBuf[tIdx+1] == '\n')
|
|
{
|
|
return (tIdx + 1);
|
|
}
|
|
}
|
|
}
|
|
// Found nothing
|
|
return -1;
|
|
}
|
|
|
|
void handleClient(int pSock, char *pPassword, char *pHWADDR)
|
|
{
|
|
slog(LOG_DEBUG_VV, "In Handle Client\n");
|
|
fflush(stdout);
|
|
|
|
socklen_t len;
|
|
struct sockaddr_storage addr;
|
|
#ifdef AF_INET6
|
|
unsigned char ipbin[INET6_ADDRSTRLEN];
|
|
#else
|
|
unsigned char ipbin[INET_ADDRSTRLEN];
|
|
#endif
|
|
unsigned int ipbinlen;
|
|
int port;
|
|
char ipstr[64];
|
|
|
|
len = sizeof addr;
|
|
getsockname(pSock, (struct sockaddr*)&addr, &len);
|
|
|
|
// deal with both IPv4 and IPv6:
|
|
if (addr.ss_family == AF_INET) {
|
|
slog(LOG_DEBUG_V, "Constructing ipv4 address\n");
|
|
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
|
|
port = ntohs(s->sin_port);
|
|
inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr);
|
|
memcpy(ipbin, &s->sin_addr, 4);
|
|
ipbinlen = 4;
|
|
} else { // AF_INET6
|
|
slog(LOG_DEBUG_V, "Constructing ipv6 address\n");
|
|
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr;
|
|
port = ntohs(s->sin6_port);
|
|
inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr);
|
|
memcpy(ipbin, &s->sin6_addr, 16);
|
|
ipbinlen = 16;
|
|
}
|
|
|
|
slog(LOG_DEBUG_V, "Peer IP address: %s\n", ipstr);
|
|
slog(LOG_DEBUG_V, "Peer port : %d\n", port);
|
|
|
|
int tMoreDataNeeded = 1;
|
|
struct keyring tKeys;
|
|
struct comms tComms;
|
|
struct connection tConn;
|
|
initConnection(&tConn, &tKeys, &tComms, pSock, pPassword);
|
|
|
|
while(1)
|
|
{
|
|
tMoreDataNeeded = 1;
|
|
|
|
initBuffer(&tConn.recv, 80); // Just a random, small size to seed the buffer with.
|
|
initBuffer(&tConn.resp, 80);
|
|
|
|
int tError = FALSE;
|
|
while(1 == tMoreDataNeeded)
|
|
{
|
|
tError = readDataFromClient(pSock, &(tConn.recv));
|
|
if(!tError)
|
|
{
|
|
slog(LOG_DEBUG_VV, "Finished Reading some data from client\n");
|
|
// parse client request
|
|
tMoreDataNeeded = parseMessage(&tConn, ipbin, ipbinlen, pHWADDR);
|
|
if(1 == tMoreDataNeeded)
|
|
{
|
|
slog(LOG_DEBUG_VV, "\n\nNeed to read more data\n");
|
|
}
|
|
else if(-1 == tMoreDataNeeded) // Forked process down below ended.
|
|
{
|
|
slog(LOG_DEBUG_V, "Forked Process ended...cleaning up\n");
|
|
cleanup(&tConn);
|
|
// pSock was already closed
|
|
return;
|
|
}
|
|
// if more data needed,
|
|
}
|
|
else
|
|
{
|
|
slog(LOG_DEBUG, "Error reading from socket, closing client\n");
|
|
// Error reading data....quit.
|
|
cleanup(&tConn);
|
|
return;
|
|
}
|
|
}
|
|
slog(LOG_DEBUG_VV, "Writing: %d chars to socket\n", tConn.resp.current);
|
|
//tConn->resp.data[tConn->resp.current-1] = '\0';
|
|
writeDataToClient(pSock, &(tConn.resp));
|
|
// Finished reading one message...
|
|
cleanupBuffers(&tConn);
|
|
}
|
|
cleanup(&tConn);
|
|
fflush(stdout);
|
|
}
|
|
|
|
|
|
void writeDataToClient(int pSock, struct shairbuffer *pResponse)
|
|
{
|
|
slog(LOG_DEBUG_VV, "\n----Beg Send Response Header----\n%.*s\n", pResponse->current, pResponse->data);
|
|
send(pSock, pResponse->data, pResponse->current,0);
|
|
slog(LOG_DEBUG_VV, "----Send Response Header----\n");
|
|
}
|
|
|
|
int readDataFromClient(int pSock, struct shairbuffer *pClientBuffer)
|
|
{
|
|
char tReadBuf[MAX_SIZE];
|
|
|
|
int tRetval = 1;
|
|
int tEnd = -1;
|
|
while(tRetval > 0 && tEnd < 0)
|
|
{
|
|
// Read from socket until \n\n, \r\n\r\n, or \r\r is found
|
|
slog(LOG_DEBUG_V, "Waiting To Read...\n");
|
|
fflush(stdout);
|
|
tRetval = read(pSock, tReadBuf, MAX_SIZE);
|
|
// if new buffer contains the end of request string, only copy partial buffer?
|
|
tEnd = findEnd(tReadBuf);
|
|
if(tEnd >= 0)
|
|
{
|
|
if(pClientBuffer->marker == 0)
|
|
{
|
|
pClientBuffer->marker = tEnd+1; // Marks start of content
|
|
}
|
|
slog(SOCKET_LOG_LEVEL, "Found end of http request at: %d\n", tEnd);
|
|
fflush(stdout);
|
|
}
|
|
else
|
|
{
|
|
tEnd = MAX_SIZE;
|
|
slog(SOCKET_LOG_LEVEL, "Read %d of data so far\n%s\n", tRetval, tReadBuf);
|
|
fflush(stdout);
|
|
}
|
|
if(tRetval > 0)
|
|
{
|
|
// Copy read data into tReceive;
|
|
slog(SOCKET_LOG_LEVEL, "Read %d data, using %d of it\n", tRetval, tEnd);
|
|
addNToShairBuffer(pClientBuffer, tReadBuf, tRetval);
|
|
slog(LOG_DEBUG_VV, "Finished copying data\n");
|
|
}
|
|
else
|
|
{
|
|
slog(LOG_DEBUG, "Error reading data from socket, got: %d bytes", tRetval);
|
|
return tRetval;
|
|
}
|
|
}
|
|
if(tEnd + 1 != tRetval)
|
|
{
|
|
slog(SOCKET_LOG_LEVEL, "Read more data after end of http request. %d instead of %d\n", tRetval, tEnd+1);
|
|
}
|
|
slog(SOCKET_LOG_LEVEL, "Finished Reading Data:\n%s\nEndOfData\n", pClientBuffer->data);
|
|
fflush(stdout);
|
|
return 0;
|
|
}
|
|
|
|
char *getFromBuffer(char *pBufferPtr, const char *pField, int pLenAfterField, int *pReturnSize, char *pDelims)
|
|
{
|
|
slog(LOG_DEBUG_V, "GettingFromBuffer: %s\n", pField);
|
|
char* tFound = strstr(pBufferPtr, pField);
|
|
int tSize = 0;
|
|
if(tFound != NULL)
|
|
{
|
|
tFound += (strlen(pField) + pLenAfterField);
|
|
int tIdx = 0;
|
|
char tDelim = pDelims[tIdx];
|
|
char *tShortest = NULL;
|
|
char *tEnd = NULL;
|
|
while(tDelim != '\0')
|
|
{
|
|
tDelim = pDelims[tIdx++]; // Ensures that \0 is also searched.
|
|
tEnd = strchr(tFound, tDelim);
|
|
if(tEnd != NULL && (NULL == tShortest || tEnd < tShortest))
|
|
{
|
|
tShortest = tEnd;
|
|
}
|
|
}
|
|
|
|
tSize = (int) (tShortest - tFound);
|
|
slog(LOG_DEBUG_VV, "Found %.*s length: %d\n", tSize, tFound, tSize);
|
|
if(pReturnSize != NULL)
|
|
{
|
|
*pReturnSize = tSize;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
slog(LOG_DEBUG_V, "Not Found\n");
|
|
}
|
|
return tFound;
|
|
}
|
|
|
|
|
|
char *getFromHeader(char *pHeaderPtr, const char *pField, int *pReturnSize)
|
|
{
|
|
return getFromBuffer(pHeaderPtr, pField, 2, pReturnSize, "\r\n");
|
|
}
|
|
|
|
char *getFromContent(char *pContentPtr, const char* pField, int *pReturnSize)
|
|
{
|
|
return getFromBuffer(pContentPtr, pField, 1, pReturnSize, "\r\n");
|
|
}
|
|
|
|
char *getFromSetup(char *pContentPtr, const char* pField, int *pReturnSize)
|
|
{
|
|
return getFromBuffer(pContentPtr, pField, 1, pReturnSize, ";\r\n");
|
|
}
|
|
|
|
// Handles compiling the Apple-Challenge, HWID, and Server IP Address
|
|
// Into the response the airplay client is expecting.
|
|
int buildAppleResponse(struct connection *pConn, unsigned char *pIpBin, unsigned int pIpBinLen, char *pHWID)
|
|
{
|
|
// Find Apple-Challenge
|
|
char *tResponse = NULL;
|
|
|
|
int tFoundSize = 0;
|
|
char* tFound = getFromHeader(pConn->recv.data, "Apple-Challenge", &tFoundSize);
|
|
if(tFound != NULL)
|
|
{
|
|
char tTrim[tFoundSize + 2];
|
|
getTrimmed(tFound, tFoundSize, TRUE, TRUE, tTrim);
|
|
slog(LOG_DEBUG_VV, "HeaderChallenge: [%s] len: %d sizeFound: %d\n", tTrim, strlen(tTrim), tFoundSize);
|
|
int tChallengeDecodeSize = 16;
|
|
char *tChallenge = decode_base64((unsigned char *)tTrim, tFoundSize, &tChallengeDecodeSize);
|
|
slog(LOG_DEBUG_VV, "Challenge Decode size: %d expected 16\n", tChallengeDecodeSize);
|
|
|
|
int tCurSize = 0;
|
|
unsigned char tChalResp[38];
|
|
|
|
memcpy(tChalResp, tChallenge, tChallengeDecodeSize);
|
|
tCurSize += tChallengeDecodeSize;
|
|
|
|
memcpy(tChalResp+tCurSize, pIpBin, pIpBinLen);
|
|
tCurSize += pIpBinLen;
|
|
|
|
memcpy(tChalResp+tCurSize, pHWID, HWID_SIZE);
|
|
tCurSize += HWID_SIZE;
|
|
|
|
int tPad = 32 - tCurSize;
|
|
if (tPad > 0)
|
|
{
|
|
memset(tChalResp+tCurSize, 0, tPad);
|
|
tCurSize += tPad;
|
|
}
|
|
|
|
char *tTmp = encode_base64((unsigned char *)tChalResp, tCurSize);
|
|
slog(LOG_DEBUG_VV, "Full sig: %s\n", tTmp);
|
|
free(tTmp);
|
|
|
|
// RSA Encrypt
|
|
RSA *rsa = loadKey(); // Free RSA
|
|
int tSize = RSA_size(rsa);
|
|
unsigned char tTo[tSize];
|
|
RSA_private_encrypt(tCurSize, (unsigned char *)tChalResp, tTo, rsa, RSA_PKCS1_PADDING);
|
|
|
|
// Wrap RSA Encrypted binary in Base64 encoding
|
|
tResponse = encode_base64(tTo, tSize);
|
|
int tLen = strlen(tResponse);
|
|
while(tLen > 1 && tResponse[tLen-1] == '=')
|
|
{
|
|
tResponse[tLen-1] = '\0';
|
|
}
|
|
free(tChallenge);
|
|
RSA_free(rsa);
|
|
}
|
|
|
|
if(tResponse != NULL)
|
|
{
|
|
// Append to current response
|
|
addToShairBuffer(&(pConn->resp), "Apple-Response: ");
|
|
addToShairBuffer(&(pConn->resp), tResponse);
|
|
addToShairBuffer(&(pConn->resp), "\r\n");
|
|
free(tResponse);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//parseMessage(tConn->recv.data, tConn->recv.mark, &tConn->resp, ipstr, pHWADDR, tConn->keys);
|
|
int parseMessage(struct connection *pConn, unsigned char *pIpBin, unsigned int pIpBinLen, char *pHWID)
|
|
{
|
|
int tReturn = 0; // 0 = good, 1 = Needs More Data, -1 = close client socket.
|
|
if(pConn->resp.data == NULL)
|
|
{
|
|
initBuffer(&(pConn->resp), MAX_SIZE);
|
|
}
|
|
|
|
char *tContent = getFromHeader(pConn->recv.data, "Content-Length", NULL);
|
|
if(tContent != NULL)
|
|
{
|
|
int tContentSize = atoi(tContent);
|
|
if(pConn->recv.marker == 0 || strlen(pConn->recv.data+pConn->recv.marker) != tContentSize)
|
|
{
|
|
if(isLogEnabledFor(HEADER_LOG_LEVEL))
|
|
{
|
|
slog(HEADER_LOG_LEVEL, "Content-Length: %s value -> %d\n", tContent, tContentSize);
|
|
if(pConn->recv.marker != 0)
|
|
{
|
|
slog(HEADER_LOG_LEVEL, "ContentPtr has %d, but needs %d\n",
|
|
strlen(pConn->recv.data+pConn->recv.marker), tContentSize);
|
|
}
|
|
}
|
|
// check if value in tContent > 2nd read from client.
|
|
return 1; // means more content-length needed
|
|
}
|
|
}
|
|
else
|
|
{
|
|
slog(LOG_DEBUG_VV, "No content, header only\n");
|
|
}
|
|
|
|
// "Creates" a new Response Header for our response message
|
|
addToShairBuffer(&(pConn->resp), "RTSP/1.0 200 OK\r\n");
|
|
|
|
if(isLogEnabledFor(LOG_INFO))
|
|
{
|
|
int tLen = strchr(pConn->recv.data, ' ') - pConn->recv.data;
|
|
if(tLen < 0 || tLen > 20)
|
|
{
|
|
tLen = 20;
|
|
}
|
|
slog(LOG_INFO, "********** RECV %.*s **********\n", tLen, pConn->recv.data);
|
|
}
|
|
|
|
if(pConn->password != NULL)
|
|
{
|
|
|
|
}
|
|
|
|
if(buildAppleResponse(pConn, pIpBin, pIpBinLen, pHWID)) // need to free sig
|
|
{
|
|
slog(LOG_DEBUG_V, "Added AppleResponse to Apple-Challenge request\n");
|
|
}
|
|
|
|
// Find option, then based on option, do different actions.
|
|
if(strncmp(pConn->recv.data, "OPTIONS", 7) == 0)
|
|
{
|
|
propogateCSeq(pConn);
|
|
addToShairBuffer(&(pConn->resp),
|
|
"Public: ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER\r\n");
|
|
}
|
|
else if(!strncmp(pConn->recv.data, "ANNOUNCE", 8))
|
|
{
|
|
char *tContent = pConn->recv.data + pConn->recv.marker;
|
|
int tSize = 0;
|
|
char *tHeaderVal = getFromContent(tContent, "a=aesiv", &tSize); // Not allocated memory, just pointing
|
|
if(tSize > 0)
|
|
{
|
|
int tKeySize = 0;
|
|
char tEncodedAesIV[tSize + 2];
|
|
getTrimmed(tHeaderVal, tSize, TRUE, TRUE, tEncodedAesIV);
|
|
slog(LOG_DEBUG_VV, "AESIV: [%.*s] Size: %d Strlen: %d\n", tSize, tEncodedAesIV, tSize, strlen(tEncodedAesIV));
|
|
char *tDecodedIV = decode_base64((unsigned char*) tEncodedAesIV, tSize, &tSize);
|
|
|
|
// grab the key, copy it out of the receive buffer
|
|
tHeaderVal = getFromContent(tContent, "a=rsaaeskey", &tKeySize);
|
|
char tEncodedAesKey[tKeySize + 2]; // +1 for nl, +1 for \0
|
|
getTrimmed(tHeaderVal, tKeySize, TRUE, TRUE, tEncodedAesKey);
|
|
slog(LOG_DEBUG_VV, "AES KEY: [%s] Size: %d Strlen: %d\n", tEncodedAesKey, tKeySize, strlen(tEncodedAesKey));
|
|
// remove base64 coding from key
|
|
char *tDecodedAesKey = decode_base64((unsigned char*) tEncodedAesKey,
|
|
tKeySize, &tKeySize); // Need to free DecodedAesKey
|
|
|
|
// Grab the formats
|
|
int tFmtpSize = 0;
|
|
char *tFmtp = getFromContent(tContent, "a=fmtp", &tFmtpSize); // Don't need to free
|
|
tFmtp = getTrimmedMalloc(tFmtp, tFmtpSize, TRUE, FALSE); // will need to free
|
|
slog(LOG_DEBUG_VV, "Format: %s\n", tFmtp);
|
|
|
|
RSA *rsa = loadKey();
|
|
// Decrypt the binary aes key
|
|
char *tDecryptedKey = malloc(RSA_size(rsa) * sizeof(char)); // Need to Free Decrypted key
|
|
//char tDecryptedKey[RSA_size(rsa)];
|
|
if(RSA_private_decrypt(tKeySize, (unsigned char *)tDecodedAesKey,
|
|
(unsigned char*) tDecryptedKey, rsa, RSA_PKCS1_OAEP_PADDING) >= 0)
|
|
{
|
|
slog(LOG_DEBUG, "Decrypted AES key from RSA Successfully\n");
|
|
}
|
|
else
|
|
{
|
|
slog(LOG_INFO, "Error Decrypting AES key from RSA\n");
|
|
}
|
|
free(tDecodedAesKey);
|
|
RSA_free(rsa);
|
|
|
|
setKeys(pConn->keys, tDecodedIV, tDecryptedKey, tFmtp);
|
|
|
|
propogateCSeq(pConn);
|
|
}
|
|
}
|
|
else if(!strncmp(pConn->recv.data, "SETUP", 5))
|
|
{
|
|
// Setup pipes
|
|
struct comms *tComms = pConn->hairtunes;
|
|
if (! (pipe(tComms->in) == 0 && pipe(tComms->out) == 0))
|
|
{
|
|
slog(LOG_INFO, "Error setting up hairtunes communications...some things probably wont work very well.\n");
|
|
}
|
|
|
|
// Setup fork
|
|
char tPort[8] = "6000"; // get this from dup()'d stdout of child pid
|
|
|
|
int tPid = fork();
|
|
if(tPid == 0)
|
|
{
|
|
int tDataport=0;
|
|
char tCPortStr[8] = "59010";
|
|
char tTPortStr[8] = "59012";
|
|
int tSize = 0;
|
|
|
|
char *tFound =getFromSetup(pConn->recv.data, "control_port", &tSize);
|
|
getTrimmed(tFound, tSize, 1, 0, tCPortStr);
|
|
tFound = getFromSetup(pConn->recv.data, "timing_port", &tSize);
|
|
getTrimmed(tFound, tSize, 1, 0, tTPortStr);
|
|
|
|
slog(LOG_DEBUG_VV, "converting %s and %s from str->int\n", tCPortStr, tTPortStr);
|
|
int tControlport = atoi(tCPortStr);
|
|
int tTimingport = atoi(tTPortStr);
|
|
|
|
slog(LOG_DEBUG_V, "Got %d for CPort and %d for TPort\n", tControlport, tTimingport);
|
|
char *tRtp = NULL;
|
|
char *tPipe = NULL;
|
|
char *tAoDriver = NULL;
|
|
char *tAoDeviceName = NULL;
|
|
char *tAoDeviceId = NULL;
|
|
|
|
// *************************************************
|
|
// ** Setting up Pipes, AKA no more debug/output **
|
|
// *************************************************
|
|
dup2(tComms->in[0],0); // Input to child
|
|
closePipe(&(tComms->in[0]));
|
|
closePipe(&(tComms->in[1]));
|
|
|
|
dup2(tComms->out[1], 1); // Output from child
|
|
closePipe(&(tComms->out[1]));
|
|
closePipe(&(tComms->out[0]));
|
|
|
|
struct keyring *tKeys = pConn->keys;
|
|
pConn->keys = NULL;
|
|
pConn->hairtunes = NULL;
|
|
|
|
// Free up any recv buffers, etc..
|
|
if(pConn->clientSocket != -1)
|
|
{
|
|
close(pConn->clientSocket);
|
|
pConn->clientSocket = -1;
|
|
}
|
|
cleanupBuffers(pConn);
|
|
hairtunes_init(tKeys->aeskey, tKeys->aesiv, tKeys->fmt, tControlport, tTimingport,
|
|
tDataport, tRtp, tPipe, tAoDriver, tAoDeviceName, tAoDeviceId);
|
|
|
|
// Quit when finished.
|
|
slog(LOG_DEBUG, "Returned from hairtunes init....returning -1, should close out this whole side of the fork\n");
|
|
return -1;
|
|
}
|
|
else if(tPid >0)
|
|
{
|
|
// Ensure Connection has access to the pipe.
|
|
closePipe(&(tComms->in[0]));
|
|
closePipe(&(tComms->out[1]));
|
|
|
|
char tFromHairtunes[80];
|
|
int tRead = read(tComms->out[0], tFromHairtunes, 80);
|
|
if(tRead <= 0)
|
|
{
|
|
slog(LOG_INFO, "Error reading port from hairtunes function, assuming default port: %d\n", tPort);
|
|
}
|
|
else
|
|
{
|
|
int tSize = 0;
|
|
char *tPortStr = getFromHeader(tFromHairtunes, "port", &tSize);
|
|
if(tPortStr != NULL)
|
|
{
|
|
getTrimmed(tPortStr, tSize, TRUE, FALSE, tPort);
|
|
}
|
|
else
|
|
{
|
|
slog(LOG_INFO, "Read %d bytes, Error translating %s into a port\n", tRead, tFromHairtunes);
|
|
}
|
|
}
|
|
// READ Ports from here?close(pConn->hairtunes_pipes[0]);
|
|
propogateCSeq(pConn);
|
|
int tSize = 0;
|
|
char *tTransport = getFromHeader(pConn->recv.data, "Transport", &tSize);
|
|
addToShairBuffer(&(pConn->resp), "Transport: ");
|
|
addNToShairBuffer(&(pConn->resp), tTransport, tSize);
|
|
// Append server port:
|
|
addToShairBuffer(&(pConn->resp), ";server_port=");
|
|
addToShairBuffer(&(pConn->resp), tPort);
|
|
addToShairBuffer(&(pConn->resp), "\r\nSession: DEADBEEF\r\n");
|
|
}
|
|
else
|
|
{
|
|
slog(LOG_INFO, "Error forking process....dere' be errors round here.\n");
|
|
return -1;
|
|
}
|
|
}
|
|
else if(!strncmp(pConn->recv.data, "TEARDOWN", 8))
|
|
{
|
|
// Be smart? Do more finish up stuff...
|
|
addToShairBuffer(&(pConn->resp), "Connection: close\r\n");
|
|
propogateCSeq(pConn);
|
|
close(pConn->hairtunes->in[1]);
|
|
slog(LOG_DEBUG, "Tearing down connection, closing pipes\n");
|
|
//close(pConn->hairtunes->out[0]);
|
|
tReturn = -1; // Close client socket, but sends an ACK/OK packet first
|
|
}
|
|
else if(!strncmp(pConn->recv.data, "FLUSH", 5))
|
|
{
|
|
write(pConn->hairtunes->in[1], "flush\n", 6);
|
|
propogateCSeq(pConn);
|
|
}
|
|
else if(!strncmp(pConn->recv.data, "SET_PARAMETER", 13))
|
|
{
|
|
propogateCSeq(pConn);
|
|
int tSize = 0;
|
|
char *tVol = getFromHeader(pConn->recv.data, "volume", &tSize);
|
|
slog(LOG_DEBUG_VV, "About to write [vol: %.*s] data to hairtunes\n", tSize, tVol);
|
|
|
|
write(pConn->hairtunes->in[1], "vol: ", 5);
|
|
write(pConn->hairtunes->in[1], tVol, tSize);
|
|
write(pConn->hairtunes->in[1], "\n", 1);
|
|
slog(LOG_DEBUG_VV, "Finished writing data write data to hairtunes\n");
|
|
}
|
|
else
|
|
{
|
|
slog(LOG_DEBUG, "\n\nUn-Handled recv: %s\n", pConn->recv.data);
|
|
propogateCSeq(pConn);
|
|
}
|
|
addToShairBuffer(&(pConn->resp), "\r\n");
|
|
return tReturn;
|
|
}
|
|
|
|
// Copies CSeq value from request, and adds standard header values in.
|
|
void propogateCSeq(struct connection *pConn) //char *pRecvBuffer, struct shairbuffer *pConn->recp.data)
|
|
{
|
|
int tSize=0;
|
|
char *tRecPtr = getFromHeader(pConn->recv.data, "CSeq", &tSize);
|
|
addToShairBuffer(&(pConn->resp), "Audio-Jack-Status: connected; type=analog\r\n");
|
|
addToShairBuffer(&(pConn->resp), "CSeq: ");
|
|
addNToShairBuffer(&(pConn->resp), tRecPtr, tSize);
|
|
addToShairBuffer(&(pConn->resp), "\r\n");
|
|
}
|
|
|
|
void cleanupBuffers(struct connection *pConn)
|
|
{
|
|
if(pConn->recv.data != NULL)
|
|
{
|
|
free(pConn->recv.data);
|
|
pConn->recv.data = NULL;
|
|
}
|
|
if(pConn->resp.data != NULL)
|
|
{
|
|
free(pConn->resp.data);
|
|
pConn->resp.data = NULL;
|
|
}
|
|
}
|
|
|
|
void cleanup(struct connection *pConn)
|
|
{
|
|
cleanupBuffers(pConn);
|
|
if(pConn->hairtunes != NULL)
|
|
{
|
|
|
|
closePipe(&(pConn->hairtunes->in[0]));
|
|
closePipe(&(pConn->hairtunes->in[1]));
|
|
closePipe(&(pConn->hairtunes->out[0]));
|
|
closePipe(&(pConn->hairtunes->out[1]));
|
|
}
|
|
if(pConn->keys != NULL)
|
|
{
|
|
if(pConn->keys->aesiv != NULL)
|
|
{
|
|
free(pConn->keys->aesiv);
|
|
}
|
|
if(pConn->keys->aeskey != NULL)
|
|
{
|
|
free(pConn->keys->aeskey);
|
|
}
|
|
if(pConn->keys->fmt != NULL)
|
|
{
|
|
free(pConn->keys->fmt);
|
|
}
|
|
pConn->keys = NULL;
|
|
}
|
|
if(pConn->clientSocket != -1)
|
|
{
|
|
close(pConn->clientSocket);
|
|
pConn->clientSocket = -1;
|
|
}
|
|
}
|
|
|
|
int startAvahi(const char *pHWStr, const char *pServerName, int pPort)
|
|
{
|
|
int tMaxServerName = 25; // Something reasonable? iPad showed 21, iphone 25
|
|
int tPid = fork();
|
|
if(tPid == 0)
|
|
{
|
|
char tName[100 + HWID_SIZE + 3];
|
|
if(strlen(pServerName) > tMaxServerName)
|
|
{
|
|
slog(LOG_INFO,"Hey dog, we see you like long server names, "
|
|
"so we put a strncat in our command so we don't buffer overflow, while you listen to your flow.\n"
|
|
"We just used the first %d characters. Pick something shorter if you want\n", tMaxServerName);
|
|
}
|
|
|
|
tName[0] = '\0';
|
|
char tPort[SERVLEN];
|
|
sprintf(tPort, "%d", pPort);
|
|
strcat(tName, pHWStr);
|
|
strcat(tName, "@");
|
|
strncat(tName, pServerName, tMaxServerName);
|
|
slog(AVAHI_LOG_LEVEL, "Avahi/DNS-SD Name: %s\n", tName);
|
|
|
|
execlp("avahi-publish-service", "avahi-publish-service", tName,
|
|
"_raop._tcp", tPort, "tp=UDP","sm=false","sv=false","ek=1","et=0,1",
|
|
"cn=0,1","ch=2","ss=16","sr=44100","pw=false","vn=3","txtvers=1", NULL);
|
|
execlp("dns-sd", "dns-sd", "-R", tName,
|
|
"_raop._tcp", ".", tPort, "tp=UDP","sm=false","sv=false","ek=1","et=0,1",
|
|
"cn=0,1","ch=2","ss=16","sr=44100","pw=false","vn=3","txtvers=1", NULL);
|
|
if(errno == -1) {
|
|
perror("error");
|
|
}
|
|
|
|
slog(LOG_INFO, "Bad error... couldn't find or failed to run: avahi-publish-service OR dns-sd\n");
|
|
exit(1);
|
|
}
|
|
else
|
|
{
|
|
slog(LOG_DEBUG_VV, "Avahi/DNS-SD started on PID: %d\n", tPid);
|
|
}
|
|
return tPid;
|
|
}
|
|
|
|
void printBufferInfo(struct shairbuffer *pBuf, int pLevel)
|
|
{
|
|
slog(pLevel, "Buffer: [%s] size: %d maxchars:%d\n", pBuf->data, pBuf->current, pBuf->maxsize/sizeof(char));
|
|
}
|
|
|
|
int getAvailChars(struct shairbuffer *pBuf)
|
|
{
|
|
return (pBuf->maxsize / sizeof(char)) - pBuf->current;
|
|
}
|
|
|
|
void addToShairBuffer(struct shairbuffer *pBuf, char *pNewBuf)
|
|
{
|
|
addNToShairBuffer(pBuf, pNewBuf, strlen(pNewBuf));
|
|
}
|
|
|
|
void addNToShairBuffer(struct shairbuffer *pBuf, char *pNewBuf, int pNofNewBuf)
|
|
{
|
|
int tAvailChars = getAvailChars(pBuf);
|
|
if(pNofNewBuf > tAvailChars)
|
|
{
|
|
int tNewSize = pBuf->maxsize * 2 + MAX_SIZE + sizeof(char);
|
|
char *tTmpBuf = malloc(tNewSize);
|
|
|
|
tTmpBuf[0] = '\0';
|
|
memset(tTmpBuf, 0, tNewSize/sizeof(char));
|
|
memcpy(tTmpBuf, pBuf->data, pBuf->current);
|
|
free(pBuf->data);
|
|
|
|
pBuf->maxsize = tNewSize;
|
|
pBuf->data = tTmpBuf;
|
|
}
|
|
memcpy(pBuf->data + pBuf->current, pNewBuf, pNofNewBuf);
|
|
pBuf->current += pNofNewBuf;
|
|
if(getAvailChars(pBuf) > 1)
|
|
{
|
|
pBuf->data[pBuf->current] = '\0';
|
|
}
|
|
}
|
|
|
|
char *getTrimmedMalloc(char *pChar, int pSize, int pEndStr, int pAddNL)
|
|
{
|
|
int tAdditionalSize = 0;
|
|
if(pEndStr)
|
|
tAdditionalSize++;
|
|
if(pAddNL)
|
|
tAdditionalSize++;
|
|
char *tTrimDest = malloc(sizeof(char) * (pSize + tAdditionalSize));
|
|
return getTrimmed(pChar, pSize, pEndStr, pAddNL, tTrimDest);
|
|
}
|
|
|
|
// Must free returned ptr
|
|
char *getTrimmed(char *pChar, int pSize, int pEndStr, int pAddNL, char *pTrimDest)
|
|
{
|
|
int tSize = pSize;
|
|
if(pEndStr)
|
|
{
|
|
tSize++;
|
|
}
|
|
if(pAddNL)
|
|
{
|
|
tSize++;
|
|
}
|
|
|
|
memset(pTrimDest, 0, tSize);
|
|
memcpy(pTrimDest, pChar, pSize);
|
|
if(pAddNL)
|
|
{
|
|
pTrimDest[pSize] = '\n';
|
|
}
|
|
if(pEndStr)
|
|
{
|
|
pTrimDest[tSize-1] = '\0';
|
|
}
|
|
return pTrimDest;
|
|
}
|
|
|
|
void slog(int pLevel, char *pFormat, ...)
|
|
{
|
|
#ifdef SHAIRPORT_LOG
|
|
if(isLogEnabledFor(pLevel))
|
|
{
|
|
va_list argp;
|
|
va_start(argp, pFormat);
|
|
vprintf(pFormat, argp);
|
|
va_end(argp);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
int isLogEnabledFor(int pLevel)
|
|
{
|
|
if(pLevel <= kCurrentLogLevel)
|
|
{
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void initConnection(struct connection *pConn, struct keyring *pKeys,
|
|
struct comms *pComms, int pSocket, char *pPassword)
|
|
{
|
|
pConn->hairtunes = pComms;
|
|
if(pKeys != NULL)
|
|
{
|
|
pConn->keys = pKeys;
|
|
pConn->keys->aesiv = NULL;
|
|
pConn->keys->aeskey = NULL;
|
|
pConn->keys->fmt = NULL;
|
|
}
|
|
pConn->recv.data = NULL; // Pre-init buffer expected to be NULL
|
|
pConn->resp.data = NULL; // Pre-init buffer expected to be NULL
|
|
pConn->clientSocket = pSocket;
|
|
if(strlen(pPassword) >0)
|
|
{
|
|
pConn->password = pPassword;
|
|
}
|
|
else
|
|
{
|
|
pConn->password = NULL;
|
|
}
|
|
}
|
|
|
|
void closePipe(int *pPipe)
|
|
{
|
|
if(*pPipe != -1)
|
|
{
|
|
close(*pPipe);
|
|
*pPipe = -1;
|
|
}
|
|
}
|
|
|
|
void initBuffer(struct shairbuffer *pBuf, int pNumChars)
|
|
{
|
|
if(pBuf->data != NULL)
|
|
{
|
|
slog(LOG_DEBUG_VV, "Hrm, buffer wasn't cleaned up....trying to free\n");
|
|
free(pBuf->data);
|
|
slog(LOG_DEBUG_VV, "Free didn't seem to seg fault....huzzah\n");
|
|
}
|
|
pBuf->current = 0;
|
|
pBuf->marker = 0;
|
|
pBuf->maxsize = sizeof(char) * pNumChars;
|
|
pBuf->data = malloc(pBuf->maxsize);
|
|
memset(pBuf->data, 0, pBuf->maxsize);
|
|
}
|
|
|
|
void setKeys(struct keyring *pKeys, char *pIV, char* pAESKey, char *pFmtp)
|
|
{
|
|
if(pKeys->aesiv != NULL)
|
|
{
|
|
free(pKeys->aesiv);
|
|
}
|
|
if(pKeys->aeskey != NULL)
|
|
{
|
|
free(pKeys->aeskey);
|
|
}
|
|
if(pKeys->fmt != NULL)
|
|
{
|
|
free(pKeys->fmt);
|
|
}
|
|
pKeys->aesiv = pIV;
|
|
pKeys->aeskey = pAESKey;
|
|
pKeys->fmt = pFmtp;
|
|
}
|
|
|
|
#define AIRPORT_PRIVATE_KEY \
|
|
"-----BEGIN RSA PRIVATE KEY-----\n" \
|
|
"MIIEpQIBAAKCAQEA59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUt\n" \
|
|
"wC5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDRKSKv6kDqnw4U\n" \
|
|
"wPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuBOitnZ/bDzPHrTOZz0Dew0uowxf\n" \
|
|
"/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJQ+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/\n" \
|
|
"UAaHqn9JdsBWLUEpVviYnhimNVvYFZeCXg/IdTQ+x4IRdiXNv5hEewIDAQABAoIBAQDl8Axy9XfW\n" \
|
|
"BLmkzkEiqoSwF0PsmVrPzH9KsnwLGH+QZlvjWd8SWYGN7u1507HvhF5N3drJoVU3O14nDY4TFQAa\n" \
|
|
"LlJ9VM35AApXaLyY1ERrN7u9ALKd2LUwYhM7Km539O4yUFYikE2nIPscEsA5ltpxOgUGCY7b7ez5\n" \
|
|
"NtD6nL1ZKauw7aNXmVAvmJTcuPxWmoktF3gDJKK2wxZuNGcJE0uFQEG4Z3BrWP7yoNuSK3dii2jm\n" \
|
|
"lpPHr0O/KnPQtzI3eguhe0TwUem/eYSdyzMyVx/YpwkzwtYL3sR5k0o9rKQLtvLzfAqdBxBurciz\n" \
|
|
"aaA/L0HIgAmOit1GJA2saMxTVPNhAoGBAPfgv1oeZxgxmotiCcMXFEQEWflzhWYTsXrhUIuz5jFu\n" \
|
|
"a39GLS99ZEErhLdrwj8rDDViRVJ5skOp9zFvlYAHs0xh92ji1E7V/ysnKBfsMrPkk5KSKPrnjndM\n" \
|
|
"oPdevWnVkgJ5jxFuNgxkOLMuG9i53B4yMvDTCRiIPMQ++N2iLDaRAoGBAO9v//mU8eVkQaoANf0Z\n" \
|
|
"oMjW8CN4xwWA2cSEIHkd9AfFkftuv8oyLDCG3ZAf0vrhrrtkrfa7ef+AUb69DNggq4mHQAYBp7L+\n" \
|
|
"k5DKzJrKuO0r+R0YbY9pZD1+/g9dVt91d6LQNepUE/yY2PP5CNoFmjedpLHMOPFdVgqDzDFxU8hL\n" \
|
|
"AoGBANDrr7xAJbqBjHVwIzQ4To9pb4BNeqDndk5Qe7fT3+/H1njGaC0/rXE0Qb7q5ySgnsCb3DvA\n" \
|
|
"cJyRM9SJ7OKlGt0FMSdJD5KG0XPIpAVNwgpXXH5MDJg09KHeh0kXo+QA6viFBi21y340NonnEfdf\n" \
|
|
"54PX4ZGS/Xac1UK+pLkBB+zRAoGAf0AY3H3qKS2lMEI4bzEFoHeK3G895pDaK3TFBVmD7fV0Zhov\n" \
|
|
"17fegFPMwOII8MisYm9ZfT2Z0s5Ro3s5rkt+nvLAdfC/PYPKzTLalpGSwomSNYJcB9HNMlmhkGzc\n" \
|
|
"1JnLYT4iyUyx6pcZBmCd8bD0iwY/FzcgNDaUmbX9+XDvRA0CgYEAkE7pIPlE71qvfJQgoA9em0gI\n" \
|
|
"LAuE4Pu13aKiJnfft7hIjbK+5kyb3TysZvoyDnb3HOKvInK7vXbKuU4ISgxB2bB3HcYzQMGsz1qJ\n" \
|
|
"2gG0N5hvJpzwwhbhXqFKA4zaaSrw622wDniAK5MlIE0tIAKKP4yxNGjoD2QYjhBGuhvkWKaXTyY=\n" \
|
|
"-----END RSA PRIVATE KEY-----"
|
|
|
|
RSA *loadKey()
|
|
{
|
|
BIO *tBio = BIO_new_mem_buf(AIRPORT_PRIVATE_KEY, -1);
|
|
RSA *rsa = PEM_read_bio_RSAPrivateKey(tBio, NULL, NULL, NULL); //NULL, NULL, NULL);
|
|
BIO_free(tBio);
|
|
slog(RSA_LOG_LEVEL, "RSA Key: %d\n", RSA_check_key(rsa));
|
|
return rsa;
|
|
}
|