Initialisierung
This commit is contained in:
0
lib/CMakeLists.txt
Normal file → Executable file
0
lib/CMakeLists.txt
Normal file → Executable file
106
lib/byteutils.c
Executable file
106
lib/byteutils.c
Executable file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2019 dsafa22, All Rights Reserved.
|
||||
*
|
||||
* 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 <time.h>
|
||||
#include <netinet/in.h>
|
||||
#include "byteutils.h"
|
||||
|
||||
#ifndef htonll
|
||||
#include <endian.h>
|
||||
#define htonll(x) htobe64(x)
|
||||
#define ntohll(x) be64toh(x)
|
||||
#endif
|
||||
|
||||
// The functions in this file assume a little endian cpu architecture!
|
||||
|
||||
/**
|
||||
* Reads a little endian unsigned 16 bit integer from the buffer at position offset
|
||||
*/
|
||||
uint16_t byteutils_get_short(unsigned char* b, int offset) {
|
||||
return *((uint16_t*)(b + offset));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a little endian unsigned 32 bit integer from the buffer at position offset
|
||||
*/
|
||||
uint32_t byteutils_get_int(unsigned char* b, int offset) {
|
||||
return *((uint32_t*)(b + offset));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a little endian unsigned 64 bit integer from the buffer at position offset
|
||||
*/
|
||||
uint64_t byteutils_get_long(unsigned char* b, int offset) {
|
||||
return *((uint64_t*)(b + offset));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a big endian unsigned 16 bit integer from the buffer at position offset
|
||||
*/
|
||||
uint16_t byteutils_get_short_be(unsigned char* b, int offset) {
|
||||
return ntohs(byteutils_get_short(b, offset));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a big endian unsigned 32 bit integer from the buffer at position offset
|
||||
*/
|
||||
uint32_t byteutils_get_int_be(unsigned char* b, int offset) {
|
||||
return ntohl(byteutils_get_int(b, offset));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a big endian unsigned 64 bit integer from the buffer at position offset
|
||||
*/
|
||||
uint64_t byteutils_get_long_be(unsigned char* b, int offset) {
|
||||
return ntohll(byteutils_get_long(b, offset));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a float from the buffer at position offset
|
||||
*/
|
||||
float byteutils_get_float(unsigned char* b, int offset) {
|
||||
return *((float*)(b + offset));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a little endian unsigned 32 bit integer to the buffer at position offset
|
||||
*/
|
||||
void byteutils_put_int(unsigned char* b, int offset, uint32_t value) {
|
||||
*((uint32_t*)(b + offset)) = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an ntp timestamp and returns it as micro seconds since the Unix epoch
|
||||
*/
|
||||
uint64_t byteutils_get_ntp_timestamp(unsigned char *b, int offset) {
|
||||
uint64_t seconds = ntohl(((unsigned int) byteutils_get_int(b, offset))) - SECONDS_FROM_1900_TO_1970;
|
||||
uint64_t fraction = ntohl((unsigned int) byteutils_get_int(b, offset + 4));
|
||||
return (seconds * 1000000L) + ((fraction * 1000000L) >> 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a time given as micro seconds since the Unix time epoch as an ntp timestamp
|
||||
* into the buffer at position offset
|
||||
*/
|
||||
void byteutils_put_ntp_timestamp(unsigned char *b, int offset, uint64_t us_since_1970) {
|
||||
uint64_t seconds = us_since_1970 / 1000000L;
|
||||
uint64_t microseconds = us_since_1970 % 1000000L;
|
||||
seconds += SECONDS_FROM_1900_TO_1970;
|
||||
uint64_t fraction = (microseconds << 32) / 1000000L;
|
||||
|
||||
// Write in big endian!
|
||||
byteutils_put_int(b, offset, htonl(seconds));
|
||||
byteutils_put_int(b, offset + 4, htonl(fraction));
|
||||
}
|
||||
|
||||
32
lib/byteutils.h
Executable file
32
lib/byteutils.h
Executable file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2019 dsafa22, All Rights Reserved.
|
||||
*
|
||||
* 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 AIRPLAYSERVER_BYTEUTILS_H
|
||||
#define AIRPLAYSERVER_BYTEUTILS_H
|
||||
#include <stdint.h>
|
||||
|
||||
uint16_t byteutils_get_short(unsigned char* b, int offset);
|
||||
uint32_t byteutils_get_int(unsigned char* b, int offset);
|
||||
uint64_t byteutils_get_long(unsigned char* b, int offset);
|
||||
uint16_t byteutils_get_short_be(unsigned char* b, int offset);
|
||||
uint32_t byteutils_get_int_be(unsigned char* b, int offset);
|
||||
uint64_t byteutils_get_long_be(unsigned char* b, int offset);
|
||||
float byteutils_get_float(unsigned char* b, int offset);
|
||||
|
||||
#define SECONDS_FROM_1900_TO_1970 2208988800ULL
|
||||
|
||||
uint64_t byteutils_get_ntp_timestamp(unsigned char *b, int offset);
|
||||
void byteutils_put_ntp_timestamp(unsigned char *b, int offset, uint64_t us_since_1970);
|
||||
|
||||
#endif //AIRPLAYSERVER_BYTEUTILS_H
|
||||
41
lib/compat.h
Executable file
41
lib/compat.h
Executable file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef COMPAT_H
|
||||
#define COMPAT_H
|
||||
|
||||
#if defined(WIN32)
|
||||
#include <ws2tcpip.h>
|
||||
#include <windows.h>
|
||||
#ifndef snprintf
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#include "memalign.h"
|
||||
#include "sockets.h"
|
||||
#include "threads.h"
|
||||
|
||||
#endif
|
||||
209
lib/crypto.c
Normal file
209
lib/crypto.c
Normal file
@@ -0,0 +1,209 @@
|
||||
/**
|
||||
* RPiPlay - An open-source AirPlay mirroring server for Raspberry Pi
|
||||
* Copyright (C) 2019 Florian Draschbacher
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "crypto.h"
|
||||
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct aes_ctx_s {
|
||||
EVP_CIPHER_CTX *cipher_ctx;
|
||||
uint8_t key[AES_128_BLOCK_SIZE];
|
||||
uint8_t iv[AES_128_BLOCK_SIZE];
|
||||
aes_direction_t direction;
|
||||
uint8_t block_offset;
|
||||
};
|
||||
|
||||
uint8_t waste[AES_128_BLOCK_SIZE];
|
||||
|
||||
// Common AES utilities
|
||||
|
||||
void handle_error(const char* location) {
|
||||
long error = ERR_get_error();
|
||||
const char* error_str = ERR_error_string(error, NULL);
|
||||
printf("Crypto error at %s: %s\n", location, error_str);
|
||||
assert(false);
|
||||
}
|
||||
|
||||
aes_ctx_t *aes_init(const uint8_t *key, const uint8_t *iv, const EVP_CIPHER *type, aes_direction_t direction) {
|
||||
aes_ctx_t *ctx = malloc(sizeof(aes_ctx_t));
|
||||
assert(ctx != NULL);
|
||||
ctx->cipher_ctx = EVP_CIPHER_CTX_new();
|
||||
assert(ctx->cipher_ctx != NULL);
|
||||
|
||||
ctx->block_offset = 0;
|
||||
ctx->direction = direction;
|
||||
|
||||
if (direction == AES_ENCRYPT) {
|
||||
if (!EVP_EncryptInit_ex(ctx->cipher_ctx, type, NULL, key, iv)) {
|
||||
handle_error(__func__);
|
||||
}
|
||||
} else {
|
||||
if (!EVP_DecryptInit_ex(ctx->cipher_ctx, type, NULL, key, iv)) {
|
||||
handle_error(__func__);
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(ctx->key, key, AES_128_BLOCK_SIZE);
|
||||
memcpy(ctx->iv, iv, AES_128_BLOCK_SIZE);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void aes_encrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int in_len) {
|
||||
int out_len = 0;
|
||||
if (!EVP_EncryptUpdate(ctx->cipher_ctx, out, &out_len, in, in_len)) {
|
||||
handle_error(__func__);
|
||||
}
|
||||
|
||||
assert(out_len <= in_len);
|
||||
}
|
||||
|
||||
void aes_decrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int in_len) {
|
||||
int out_len = 0;
|
||||
if (!EVP_DecryptUpdate(ctx->cipher_ctx, out, &out_len, in, in_len)) {
|
||||
handle_error(__func__);
|
||||
}
|
||||
|
||||
assert(out_len <= in_len);
|
||||
}
|
||||
|
||||
void aes_destroy(aes_ctx_t *ctx) {
|
||||
if (ctx) {
|
||||
EVP_CIPHER_CTX_free(ctx->cipher_ctx);
|
||||
free(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void aes_reset(aes_ctx_t *ctx, const EVP_CIPHER *type, aes_direction_t direction) {
|
||||
if (!EVP_CIPHER_CTX_reset(ctx->cipher_ctx)) {
|
||||
handle_error(__func__);
|
||||
}
|
||||
|
||||
if (direction == AES_ENCRYPT) {
|
||||
if (!EVP_EncryptInit_ex(ctx->cipher_ctx, type, NULL, ctx->key, ctx->iv)) {
|
||||
handle_error(__func__);
|
||||
}
|
||||
} else {
|
||||
if (!EVP_DecryptInit_ex(ctx->cipher_ctx, type, NULL, ctx->key, ctx->iv)) {
|
||||
handle_error(__func__);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AES CTR
|
||||
|
||||
aes_ctx_t *aes_ctr_init(const uint8_t *key, const uint8_t *iv) {
|
||||
return aes_init(key, iv, EVP_aes_128_ctr(), AES_ENCRYPT);
|
||||
}
|
||||
|
||||
void aes_ctr_encrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len) {
|
||||
aes_encrypt(ctx, in, out, len);
|
||||
ctx->block_offset = (ctx->block_offset + len) % AES_128_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
void aes_ctr_start_fresh_block(aes_ctx_t *ctx) {
|
||||
// Is there a better way to do this?
|
||||
if (ctx->block_offset == 0) return;
|
||||
aes_ctr_encrypt(ctx, waste, waste, AES_128_BLOCK_SIZE - ctx->block_offset);
|
||||
}
|
||||
|
||||
void aes_ctr_decrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len) {
|
||||
aes_encrypt(ctx, in, out, len);
|
||||
}
|
||||
|
||||
void aes_ctr_reset(aes_ctx_t *ctx) {
|
||||
aes_reset(ctx, EVP_aes_128_ctr(), AES_ENCRYPT);
|
||||
}
|
||||
|
||||
void aes_ctr_destroy(aes_ctx_t *ctx) {
|
||||
aes_destroy(ctx);
|
||||
}
|
||||
|
||||
// AES CBC
|
||||
|
||||
aes_ctx_t *aes_cbc_init(const uint8_t *key, const uint8_t *iv, aes_direction_t direction) {
|
||||
return aes_init(key, iv, EVP_aes_128_cbc(), direction);
|
||||
}
|
||||
|
||||
void aes_cbc_encrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len) {
|
||||
assert(ctx->direction == AES_ENCRYPT);
|
||||
aes_encrypt(ctx, in, out, len);
|
||||
}
|
||||
|
||||
void aes_cbc_decrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len) {
|
||||
assert(ctx->direction == AES_DECRYPT);
|
||||
aes_decrypt(ctx, in, out, len);
|
||||
}
|
||||
|
||||
void aes_cbc_reset(aes_ctx_t *ctx) {
|
||||
aes_reset(ctx, EVP_aes_128_ctr(), ctx->direction);
|
||||
}
|
||||
|
||||
void aes_cbc_destroy(aes_ctx_t *ctx) {
|
||||
aes_destroy(ctx);
|
||||
}
|
||||
|
||||
// SHA 512
|
||||
|
||||
struct sha_ctx_s {
|
||||
EVP_MD_CTX *digest_ctx;
|
||||
};
|
||||
|
||||
sha_ctx_t *sha_init() {
|
||||
sha_ctx_t *ctx = malloc(sizeof(sha_ctx_t));
|
||||
assert(ctx != NULL);
|
||||
ctx->digest_ctx = EVP_MD_CTX_new();
|
||||
assert(ctx->digest_ctx != NULL);
|
||||
|
||||
if (!EVP_DigestInit_ex(ctx->digest_ctx, EVP_sha512(), NULL)) {
|
||||
handle_error(__func__);
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void sha_update(sha_ctx_t *ctx, const uint8_t *in, int len) {
|
||||
if (!EVP_DigestUpdate(ctx->digest_ctx, in, len)) {
|
||||
handle_error(__func__);
|
||||
}
|
||||
}
|
||||
|
||||
void sha_final(sha_ctx_t *ctx, uint8_t *out, unsigned int *len) {
|
||||
if (!EVP_DigestFinal_ex(ctx->digest_ctx, out, len)) {
|
||||
handle_error(__func__);
|
||||
}
|
||||
}
|
||||
|
||||
void sha_reset(sha_ctx_t *ctx) {
|
||||
if (!EVP_MD_CTX_reset(ctx->digest_ctx) ||
|
||||
!EVP_DigestInit_ex(ctx->digest_ctx, EVP_sha512(), NULL)) {
|
||||
|
||||
handle_error(__func__);
|
||||
}
|
||||
}
|
||||
|
||||
void sha_destroy(sha_ctx_t *ctx) {
|
||||
if (ctx) {
|
||||
EVP_MD_CTX_free(ctx->digest_ctx);
|
||||
free(ctx);
|
||||
}
|
||||
}
|
||||
67
lib/crypto.h
Normal file
67
lib/crypto.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* RPiPlay - An open-source AirPlay mirroring server for Raspberry Pi
|
||||
* Copyright (C) 2019 Florian Draschbacher
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Helper methods for various crypto operations.
|
||||
* Uses OpenSSL behind the scenes.
|
||||
*/
|
||||
|
||||
#ifndef CRYPTO_H
|
||||
#define CRYPTO_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// 128bit AES in CTR mode
|
||||
|
||||
#define AES_128_BLOCK_SIZE 16
|
||||
|
||||
typedef enum aes_direction_e { AES_DECRYPT, AES_ENCRYPT } aes_direction_t;
|
||||
|
||||
typedef struct aes_ctx_s aes_ctx_t;
|
||||
|
||||
aes_ctx_t *aes_ctr_init(const uint8_t *key, const uint8_t *iv);
|
||||
void aes_ctr_reset(aes_ctx_t *ctx);
|
||||
void aes_ctr_encrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len);
|
||||
void aes_ctr_decrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len);
|
||||
void aes_ctr_start_fresh_block(aes_ctx_t *ctx);
|
||||
void aes_ctr_destroy(aes_ctx_t *ctx);
|
||||
|
||||
aes_ctx_t *aes_cbc_init(const uint8_t *key, const uint8_t *iv, aes_direction_t direction);
|
||||
void aes_cbc_reset(aes_ctx_t *ctx);
|
||||
void aes_cbc_encrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len);
|
||||
void aes_cbc_decrypt(aes_ctx_t *ctx, const uint8_t *in, uint8_t *out, int len);
|
||||
void aes_cbc_destroy(aes_ctx_t *ctx);
|
||||
|
||||
// SHA512
|
||||
|
||||
typedef struct sha_ctx_s sha_ctx_t;
|
||||
sha_ctx_t *sha_init();
|
||||
void sha_update(sha_ctx_t *ctx, const uint8_t *in, int len);
|
||||
void sha_final(sha_ctx_t *ctx, uint8_t *out, unsigned int *len);
|
||||
void sha_reset(sha_ctx_t *ctx);
|
||||
void sha_destroy(sha_ctx_t *ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
6
lib/curve25519/CMakeLists.txt
Executable file
6
lib/curve25519/CMakeLists.txt
Executable file
@@ -0,0 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.4.1)
|
||||
aux_source_directory(. curve25519_src)
|
||||
set(DIR_SRCS ${curve25519_src})
|
||||
add_library( curve25519
|
||||
STATIC
|
||||
${DIR_SRCS})
|
||||
860
lib/curve25519/curve25519-donna.c
Executable file
860
lib/curve25519/curve25519-donna.c
Executable file
@@ -0,0 +1,860 @@
|
||||
/* Copyright 2008, Google Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * 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.
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "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 COPYRIGHT
|
||||
* OWNER 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.
|
||||
*
|
||||
* curve25519-donna: Curve25519 elliptic curve, public key function
|
||||
*
|
||||
* http://code.google.com/p/curve25519-donna/
|
||||
*
|
||||
* Adam Langley <agl@imperialviolet.org>
|
||||
*
|
||||
* Derived from public domain C code by Daniel J. Bernstein <djb@cr.yp.to>
|
||||
*
|
||||
* More information about curve25519 can be found here
|
||||
* http://cr.yp.to/ecdh.html
|
||||
*
|
||||
* djb's sample implementation of curve25519 is written in a special assembly
|
||||
* language called qhasm and uses the floating point registers.
|
||||
*
|
||||
* This is, almost, a clean room reimplementation from the curve25519 paper. It
|
||||
* uses many of the tricks described therein. Only the crecip function is taken
|
||||
* from the sample implementation. */
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define inline __inline
|
||||
#endif
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef int32_t s32;
|
||||
typedef int64_t limb;
|
||||
|
||||
/* Field element representation:
|
||||
*
|
||||
* Field elements are written as an array of signed, 64-bit limbs, least
|
||||
* significant first. The value of the field element is:
|
||||
* x[0] + 2^26·x[1] + x^51·x[2] + 2^102·x[3] + ...
|
||||
*
|
||||
* i.e. the limbs are 26, 25, 26, 25, ... bits wide. */
|
||||
|
||||
/* Sum two numbers: output += in */
|
||||
static void fsum(limb *output, const limb *in) {
|
||||
unsigned i;
|
||||
for (i = 0; i < 10; i += 2) {
|
||||
output[0+i] = output[0+i] + in[0+i];
|
||||
output[1+i] = output[1+i] + in[1+i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Find the difference of two numbers: output = in - output
|
||||
* (note the order of the arguments!). */
|
||||
static void fdifference(limb *output, const limb *in) {
|
||||
unsigned i;
|
||||
for (i = 0; i < 10; ++i) {
|
||||
output[i] = in[i] - output[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Multiply a number by a scalar: output = in * scalar */
|
||||
static void fscalar_product(limb *output, const limb *in, const limb scalar) {
|
||||
unsigned i;
|
||||
for (i = 0; i < 10; ++i) {
|
||||
output[i] = in[i] * scalar;
|
||||
}
|
||||
}
|
||||
|
||||
/* Multiply two numbers: output = in2 * in
|
||||
*
|
||||
* output must be distinct to both inputs. The inputs are reduced coefficient
|
||||
* form, the output is not.
|
||||
*
|
||||
* output[x] <= 14 * the largest product of the input limbs. */
|
||||
static void fproduct(limb *output, const limb *in2, const limb *in) {
|
||||
output[0] = ((limb) ((s32) in2[0])) * ((s32) in[0]);
|
||||
output[1] = ((limb) ((s32) in2[0])) * ((s32) in[1]) +
|
||||
((limb) ((s32) in2[1])) * ((s32) in[0]);
|
||||
output[2] = 2 * ((limb) ((s32) in2[1])) * ((s32) in[1]) +
|
||||
((limb) ((s32) in2[0])) * ((s32) in[2]) +
|
||||
((limb) ((s32) in2[2])) * ((s32) in[0]);
|
||||
output[3] = ((limb) ((s32) in2[1])) * ((s32) in[2]) +
|
||||
((limb) ((s32) in2[2])) * ((s32) in[1]) +
|
||||
((limb) ((s32) in2[0])) * ((s32) in[3]) +
|
||||
((limb) ((s32) in2[3])) * ((s32) in[0]);
|
||||
output[4] = ((limb) ((s32) in2[2])) * ((s32) in[2]) +
|
||||
2 * (((limb) ((s32) in2[1])) * ((s32) in[3]) +
|
||||
((limb) ((s32) in2[3])) * ((s32) in[1])) +
|
||||
((limb) ((s32) in2[0])) * ((s32) in[4]) +
|
||||
((limb) ((s32) in2[4])) * ((s32) in[0]);
|
||||
output[5] = ((limb) ((s32) in2[2])) * ((s32) in[3]) +
|
||||
((limb) ((s32) in2[3])) * ((s32) in[2]) +
|
||||
((limb) ((s32) in2[1])) * ((s32) in[4]) +
|
||||
((limb) ((s32) in2[4])) * ((s32) in[1]) +
|
||||
((limb) ((s32) in2[0])) * ((s32) in[5]) +
|
||||
((limb) ((s32) in2[5])) * ((s32) in[0]);
|
||||
output[6] = 2 * (((limb) ((s32) in2[3])) * ((s32) in[3]) +
|
||||
((limb) ((s32) in2[1])) * ((s32) in[5]) +
|
||||
((limb) ((s32) in2[5])) * ((s32) in[1])) +
|
||||
((limb) ((s32) in2[2])) * ((s32) in[4]) +
|
||||
((limb) ((s32) in2[4])) * ((s32) in[2]) +
|
||||
((limb) ((s32) in2[0])) * ((s32) in[6]) +
|
||||
((limb) ((s32) in2[6])) * ((s32) in[0]);
|
||||
output[7] = ((limb) ((s32) in2[3])) * ((s32) in[4]) +
|
||||
((limb) ((s32) in2[4])) * ((s32) in[3]) +
|
||||
((limb) ((s32) in2[2])) * ((s32) in[5]) +
|
||||
((limb) ((s32) in2[5])) * ((s32) in[2]) +
|
||||
((limb) ((s32) in2[1])) * ((s32) in[6]) +
|
||||
((limb) ((s32) in2[6])) * ((s32) in[1]) +
|
||||
((limb) ((s32) in2[0])) * ((s32) in[7]) +
|
||||
((limb) ((s32) in2[7])) * ((s32) in[0]);
|
||||
output[8] = ((limb) ((s32) in2[4])) * ((s32) in[4]) +
|
||||
2 * (((limb) ((s32) in2[3])) * ((s32) in[5]) +
|
||||
((limb) ((s32) in2[5])) * ((s32) in[3]) +
|
||||
((limb) ((s32) in2[1])) * ((s32) in[7]) +
|
||||
((limb) ((s32) in2[7])) * ((s32) in[1])) +
|
||||
((limb) ((s32) in2[2])) * ((s32) in[6]) +
|
||||
((limb) ((s32) in2[6])) * ((s32) in[2]) +
|
||||
((limb) ((s32) in2[0])) * ((s32) in[8]) +
|
||||
((limb) ((s32) in2[8])) * ((s32) in[0]);
|
||||
output[9] = ((limb) ((s32) in2[4])) * ((s32) in[5]) +
|
||||
((limb) ((s32) in2[5])) * ((s32) in[4]) +
|
||||
((limb) ((s32) in2[3])) * ((s32) in[6]) +
|
||||
((limb) ((s32) in2[6])) * ((s32) in[3]) +
|
||||
((limb) ((s32) in2[2])) * ((s32) in[7]) +
|
||||
((limb) ((s32) in2[7])) * ((s32) in[2]) +
|
||||
((limb) ((s32) in2[1])) * ((s32) in[8]) +
|
||||
((limb) ((s32) in2[8])) * ((s32) in[1]) +
|
||||
((limb) ((s32) in2[0])) * ((s32) in[9]) +
|
||||
((limb) ((s32) in2[9])) * ((s32) in[0]);
|
||||
output[10] = 2 * (((limb) ((s32) in2[5])) * ((s32) in[5]) +
|
||||
((limb) ((s32) in2[3])) * ((s32) in[7]) +
|
||||
((limb) ((s32) in2[7])) * ((s32) in[3]) +
|
||||
((limb) ((s32) in2[1])) * ((s32) in[9]) +
|
||||
((limb) ((s32) in2[9])) * ((s32) in[1])) +
|
||||
((limb) ((s32) in2[4])) * ((s32) in[6]) +
|
||||
((limb) ((s32) in2[6])) * ((s32) in[4]) +
|
||||
((limb) ((s32) in2[2])) * ((s32) in[8]) +
|
||||
((limb) ((s32) in2[8])) * ((s32) in[2]);
|
||||
output[11] = ((limb) ((s32) in2[5])) * ((s32) in[6]) +
|
||||
((limb) ((s32) in2[6])) * ((s32) in[5]) +
|
||||
((limb) ((s32) in2[4])) * ((s32) in[7]) +
|
||||
((limb) ((s32) in2[7])) * ((s32) in[4]) +
|
||||
((limb) ((s32) in2[3])) * ((s32) in[8]) +
|
||||
((limb) ((s32) in2[8])) * ((s32) in[3]) +
|
||||
((limb) ((s32) in2[2])) * ((s32) in[9]) +
|
||||
((limb) ((s32) in2[9])) * ((s32) in[2]);
|
||||
output[12] = ((limb) ((s32) in2[6])) * ((s32) in[6]) +
|
||||
2 * (((limb) ((s32) in2[5])) * ((s32) in[7]) +
|
||||
((limb) ((s32) in2[7])) * ((s32) in[5]) +
|
||||
((limb) ((s32) in2[3])) * ((s32) in[9]) +
|
||||
((limb) ((s32) in2[9])) * ((s32) in[3])) +
|
||||
((limb) ((s32) in2[4])) * ((s32) in[8]) +
|
||||
((limb) ((s32) in2[8])) * ((s32) in[4]);
|
||||
output[13] = ((limb) ((s32) in2[6])) * ((s32) in[7]) +
|
||||
((limb) ((s32) in2[7])) * ((s32) in[6]) +
|
||||
((limb) ((s32) in2[5])) * ((s32) in[8]) +
|
||||
((limb) ((s32) in2[8])) * ((s32) in[5]) +
|
||||
((limb) ((s32) in2[4])) * ((s32) in[9]) +
|
||||
((limb) ((s32) in2[9])) * ((s32) in[4]);
|
||||
output[14] = 2 * (((limb) ((s32) in2[7])) * ((s32) in[7]) +
|
||||
((limb) ((s32) in2[5])) * ((s32) in[9]) +
|
||||
((limb) ((s32) in2[9])) * ((s32) in[5])) +
|
||||
((limb) ((s32) in2[6])) * ((s32) in[8]) +
|
||||
((limb) ((s32) in2[8])) * ((s32) in[6]);
|
||||
output[15] = ((limb) ((s32) in2[7])) * ((s32) in[8]) +
|
||||
((limb) ((s32) in2[8])) * ((s32) in[7]) +
|
||||
((limb) ((s32) in2[6])) * ((s32) in[9]) +
|
||||
((limb) ((s32) in2[9])) * ((s32) in[6]);
|
||||
output[16] = ((limb) ((s32) in2[8])) * ((s32) in[8]) +
|
||||
2 * (((limb) ((s32) in2[7])) * ((s32) in[9]) +
|
||||
((limb) ((s32) in2[9])) * ((s32) in[7]));
|
||||
output[17] = ((limb) ((s32) in2[8])) * ((s32) in[9]) +
|
||||
((limb) ((s32) in2[9])) * ((s32) in[8]);
|
||||
output[18] = 2 * ((limb) ((s32) in2[9])) * ((s32) in[9]);
|
||||
}
|
||||
|
||||
/* Reduce a long form to a short form by taking the input mod 2^255 - 19.
|
||||
*
|
||||
* On entry: |output[i]| < 14*2^54
|
||||
* On exit: |output[0..8]| < 280*2^54 */
|
||||
static void freduce_degree(limb *output) {
|
||||
/* Each of these shifts and adds ends up multiplying the value by 19.
|
||||
*
|
||||
* For output[0..8], the absolute entry value is < 14*2^54 and we add, at
|
||||
* most, 19*14*2^54 thus, on exit, |output[0..8]| < 280*2^54. */
|
||||
output[8] += output[18] << 4;
|
||||
output[8] += output[18] << 1;
|
||||
output[8] += output[18];
|
||||
output[7] += output[17] << 4;
|
||||
output[7] += output[17] << 1;
|
||||
output[7] += output[17];
|
||||
output[6] += output[16] << 4;
|
||||
output[6] += output[16] << 1;
|
||||
output[6] += output[16];
|
||||
output[5] += output[15] << 4;
|
||||
output[5] += output[15] << 1;
|
||||
output[5] += output[15];
|
||||
output[4] += output[14] << 4;
|
||||
output[4] += output[14] << 1;
|
||||
output[4] += output[14];
|
||||
output[3] += output[13] << 4;
|
||||
output[3] += output[13] << 1;
|
||||
output[3] += output[13];
|
||||
output[2] += output[12] << 4;
|
||||
output[2] += output[12] << 1;
|
||||
output[2] += output[12];
|
||||
output[1] += output[11] << 4;
|
||||
output[1] += output[11] << 1;
|
||||
output[1] += output[11];
|
||||
output[0] += output[10] << 4;
|
||||
output[0] += output[10] << 1;
|
||||
output[0] += output[10];
|
||||
}
|
||||
|
||||
#if (-1 & 3) != 3
|
||||
#error "This code only works on a two's complement system"
|
||||
#endif
|
||||
|
||||
/* return v / 2^26, using only shifts and adds.
|
||||
*
|
||||
* On entry: v can take any value. */
|
||||
static inline limb
|
||||
div_by_2_26(const limb v)
|
||||
{
|
||||
/* High word of v; no shift needed. */
|
||||
const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32);
|
||||
/* Set to all 1s if v was negative; else set to 0s. */
|
||||
const int32_t sign = ((int32_t) highword) >> 31;
|
||||
/* Set to 0x3ffffff if v was negative; else set to 0. */
|
||||
const int32_t roundoff = ((uint32_t) sign) >> 6;
|
||||
/* Should return v / (1<<26) */
|
||||
return (v + roundoff) >> 26;
|
||||
}
|
||||
|
||||
/* return v / (2^25), using only shifts and adds.
|
||||
*
|
||||
* On entry: v can take any value. */
|
||||
static inline limb
|
||||
div_by_2_25(const limb v)
|
||||
{
|
||||
/* High word of v; no shift needed*/
|
||||
const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32);
|
||||
/* Set to all 1s if v was negative; else set to 0s. */
|
||||
const int32_t sign = ((int32_t) highword) >> 31;
|
||||
/* Set to 0x1ffffff if v was negative; else set to 0. */
|
||||
const int32_t roundoff = ((uint32_t) sign) >> 7;
|
||||
/* Should return v / (1<<25) */
|
||||
return (v + roundoff) >> 25;
|
||||
}
|
||||
|
||||
/* Reduce all coefficients of the short form input so that |x| < 2^26.
|
||||
*
|
||||
* On entry: |output[i]| < 280*2^54 */
|
||||
static void freduce_coefficients(limb *output) {
|
||||
unsigned i;
|
||||
|
||||
output[10] = 0;
|
||||
|
||||
for (i = 0; i < 10; i += 2) {
|
||||
limb over = div_by_2_26(output[i]);
|
||||
/* The entry condition (that |output[i]| < 280*2^54) means that over is, at
|
||||
* most, 280*2^28 in the first iteration of this loop. This is added to the
|
||||
* next limb and we can approximate the resulting bound of that limb by
|
||||
* 281*2^54. */
|
||||
output[i] -= over << 26;
|
||||
output[i+1] += over;
|
||||
|
||||
/* For the first iteration, |output[i+1]| < 281*2^54, thus |over| <
|
||||
* 281*2^29. When this is added to the next limb, the resulting bound can
|
||||
* be approximated as 281*2^54.
|
||||
*
|
||||
* For subsequent iterations of the loop, 281*2^54 remains a conservative
|
||||
* bound and no overflow occurs. */
|
||||
over = div_by_2_25(output[i+1]);
|
||||
output[i+1] -= over << 25;
|
||||
output[i+2] += over;
|
||||
}
|
||||
/* Now |output[10]| < 281*2^29 and all other coefficients are reduced. */
|
||||
output[0] += output[10] << 4;
|
||||
output[0] += output[10] << 1;
|
||||
output[0] += output[10];
|
||||
|
||||
output[10] = 0;
|
||||
|
||||
/* Now output[1..9] are reduced, and |output[0]| < 2^26 + 19*281*2^29
|
||||
* So |over| will be no more than 2^16. */
|
||||
{
|
||||
limb over = div_by_2_26(output[0]);
|
||||
output[0] -= over << 26;
|
||||
output[1] += over;
|
||||
}
|
||||
|
||||
/* Now output[0,2..9] are reduced, and |output[1]| < 2^25 + 2^16 < 2^26. The
|
||||
* bound on |output[1]| is sufficient to meet our needs. */
|
||||
}
|
||||
|
||||
/* A helpful wrapper around fproduct: output = in * in2.
|
||||
*
|
||||
* On entry: |in[i]| < 2^27 and |in2[i]| < 2^27.
|
||||
*
|
||||
* output must be distinct to both inputs. The output is reduced degree
|
||||
* (indeed, one need only provide storage for 10 limbs) and |output[i]| < 2^26. */
|
||||
static void
|
||||
fmul(limb *output, const limb *in, const limb *in2) {
|
||||
limb t[19];
|
||||
fproduct(t, in, in2);
|
||||
/* |t[i]| < 14*2^54 */
|
||||
freduce_degree(t);
|
||||
freduce_coefficients(t);
|
||||
/* |t[i]| < 2^26 */
|
||||
memcpy(output, t, sizeof(limb) * 10);
|
||||
}
|
||||
|
||||
/* Square a number: output = in**2
|
||||
*
|
||||
* output must be distinct from the input. The inputs are reduced coefficient
|
||||
* form, the output is not.
|
||||
*
|
||||
* output[x] <= 14 * the largest product of the input limbs. */
|
||||
static void fsquare_inner(limb *output, const limb *in) {
|
||||
output[0] = ((limb) ((s32) in[0])) * ((s32) in[0]);
|
||||
output[1] = 2 * ((limb) ((s32) in[0])) * ((s32) in[1]);
|
||||
output[2] = 2 * (((limb) ((s32) in[1])) * ((s32) in[1]) +
|
||||
((limb) ((s32) in[0])) * ((s32) in[2]));
|
||||
output[3] = 2 * (((limb) ((s32) in[1])) * ((s32) in[2]) +
|
||||
((limb) ((s32) in[0])) * ((s32) in[3]));
|
||||
output[4] = ((limb) ((s32) in[2])) * ((s32) in[2]) +
|
||||
4 * ((limb) ((s32) in[1])) * ((s32) in[3]) +
|
||||
2 * ((limb) ((s32) in[0])) * ((s32) in[4]);
|
||||
output[5] = 2 * (((limb) ((s32) in[2])) * ((s32) in[3]) +
|
||||
((limb) ((s32) in[1])) * ((s32) in[4]) +
|
||||
((limb) ((s32) in[0])) * ((s32) in[5]));
|
||||
output[6] = 2 * (((limb) ((s32) in[3])) * ((s32) in[3]) +
|
||||
((limb) ((s32) in[2])) * ((s32) in[4]) +
|
||||
((limb) ((s32) in[0])) * ((s32) in[6]) +
|
||||
2 * ((limb) ((s32) in[1])) * ((s32) in[5]));
|
||||
output[7] = 2 * (((limb) ((s32) in[3])) * ((s32) in[4]) +
|
||||
((limb) ((s32) in[2])) * ((s32) in[5]) +
|
||||
((limb) ((s32) in[1])) * ((s32) in[6]) +
|
||||
((limb) ((s32) in[0])) * ((s32) in[7]));
|
||||
output[8] = ((limb) ((s32) in[4])) * ((s32) in[4]) +
|
||||
2 * (((limb) ((s32) in[2])) * ((s32) in[6]) +
|
||||
((limb) ((s32) in[0])) * ((s32) in[8]) +
|
||||
2 * (((limb) ((s32) in[1])) * ((s32) in[7]) +
|
||||
((limb) ((s32) in[3])) * ((s32) in[5])));
|
||||
output[9] = 2 * (((limb) ((s32) in[4])) * ((s32) in[5]) +
|
||||
((limb) ((s32) in[3])) * ((s32) in[6]) +
|
||||
((limb) ((s32) in[2])) * ((s32) in[7]) +
|
||||
((limb) ((s32) in[1])) * ((s32) in[8]) +
|
||||
((limb) ((s32) in[0])) * ((s32) in[9]));
|
||||
output[10] = 2 * (((limb) ((s32) in[5])) * ((s32) in[5]) +
|
||||
((limb) ((s32) in[4])) * ((s32) in[6]) +
|
||||
((limb) ((s32) in[2])) * ((s32) in[8]) +
|
||||
2 * (((limb) ((s32) in[3])) * ((s32) in[7]) +
|
||||
((limb) ((s32) in[1])) * ((s32) in[9])));
|
||||
output[11] = 2 * (((limb) ((s32) in[5])) * ((s32) in[6]) +
|
||||
((limb) ((s32) in[4])) * ((s32) in[7]) +
|
||||
((limb) ((s32) in[3])) * ((s32) in[8]) +
|
||||
((limb) ((s32) in[2])) * ((s32) in[9]));
|
||||
output[12] = ((limb) ((s32) in[6])) * ((s32) in[6]) +
|
||||
2 * (((limb) ((s32) in[4])) * ((s32) in[8]) +
|
||||
2 * (((limb) ((s32) in[5])) * ((s32) in[7]) +
|
||||
((limb) ((s32) in[3])) * ((s32) in[9])));
|
||||
output[13] = 2 * (((limb) ((s32) in[6])) * ((s32) in[7]) +
|
||||
((limb) ((s32) in[5])) * ((s32) in[8]) +
|
||||
((limb) ((s32) in[4])) * ((s32) in[9]));
|
||||
output[14] = 2 * (((limb) ((s32) in[7])) * ((s32) in[7]) +
|
||||
((limb) ((s32) in[6])) * ((s32) in[8]) +
|
||||
2 * ((limb) ((s32) in[5])) * ((s32) in[9]));
|
||||
output[15] = 2 * (((limb) ((s32) in[7])) * ((s32) in[8]) +
|
||||
((limb) ((s32) in[6])) * ((s32) in[9]));
|
||||
output[16] = ((limb) ((s32) in[8])) * ((s32) in[8]) +
|
||||
4 * ((limb) ((s32) in[7])) * ((s32) in[9]);
|
||||
output[17] = 2 * ((limb) ((s32) in[8])) * ((s32) in[9]);
|
||||
output[18] = 2 * ((limb) ((s32) in[9])) * ((s32) in[9]);
|
||||
}
|
||||
|
||||
/* fsquare sets output = in^2.
|
||||
*
|
||||
* On entry: The |in| argument is in reduced coefficients form and |in[i]| <
|
||||
* 2^27.
|
||||
*
|
||||
* On exit: The |output| argument is in reduced coefficients form (indeed, one
|
||||
* need only provide storage for 10 limbs) and |out[i]| < 2^26. */
|
||||
static void
|
||||
fsquare(limb *output, const limb *in) {
|
||||
limb t[19];
|
||||
fsquare_inner(t, in);
|
||||
/* |t[i]| < 14*2^54 because the largest product of two limbs will be <
|
||||
* 2^(27+27) and fsquare_inner adds together, at most, 14 of those
|
||||
* products. */
|
||||
freduce_degree(t);
|
||||
freduce_coefficients(t);
|
||||
/* |t[i]| < 2^26 */
|
||||
memcpy(output, t, sizeof(limb) * 10);
|
||||
}
|
||||
|
||||
/* Take a little-endian, 32-byte number and expand it into polynomial form */
|
||||
static void
|
||||
fexpand(limb *output, const u8 *input) {
|
||||
#define F(n,start,shift,mask) \
|
||||
output[n] = ((((limb) input[start + 0]) | \
|
||||
((limb) input[start + 1]) << 8 | \
|
||||
((limb) input[start + 2]) << 16 | \
|
||||
((limb) input[start + 3]) << 24) >> shift) & mask;
|
||||
F(0, 0, 0, 0x3ffffff);
|
||||
F(1, 3, 2, 0x1ffffff);
|
||||
F(2, 6, 3, 0x3ffffff);
|
||||
F(3, 9, 5, 0x1ffffff);
|
||||
F(4, 12, 6, 0x3ffffff);
|
||||
F(5, 16, 0, 0x1ffffff);
|
||||
F(6, 19, 1, 0x3ffffff);
|
||||
F(7, 22, 3, 0x1ffffff);
|
||||
F(8, 25, 4, 0x3ffffff);
|
||||
F(9, 28, 6, 0x1ffffff);
|
||||
#undef F
|
||||
}
|
||||
|
||||
#if (-32 >> 1) != -16
|
||||
#error "This code only works when >> does sign-extension on negative numbers"
|
||||
#endif
|
||||
|
||||
/* s32_eq returns 0xffffffff iff a == b and zero otherwise. */
|
||||
static s32 s32_eq(s32 a, s32 b) {
|
||||
a = ~(a ^ b);
|
||||
a &= a << 16;
|
||||
a &= a << 8;
|
||||
a &= a << 4;
|
||||
a &= a << 2;
|
||||
a &= a << 1;
|
||||
return a >> 31;
|
||||
}
|
||||
|
||||
/* s32_gte returns 0xffffffff if a >= b and zero otherwise, where a and b are
|
||||
* both non-negative. */
|
||||
static s32 s32_gte(s32 a, s32 b) {
|
||||
a -= b;
|
||||
/* a >= 0 iff a >= b. */
|
||||
return ~(a >> 31);
|
||||
}
|
||||
|
||||
/* Take a fully reduced polynomial form number and contract it into a
|
||||
* little-endian, 32-byte array.
|
||||
*
|
||||
* On entry: |input_limbs[i]| < 2^26 */
|
||||
static void
|
||||
fcontract(u8 *output, limb *input_limbs) {
|
||||
int i;
|
||||
int j;
|
||||
s32 input[10];
|
||||
s32 mask;
|
||||
|
||||
/* |input_limbs[i]| < 2^26, so it's valid to convert to an s32. */
|
||||
for (i = 0; i < 10; i++) {
|
||||
input[i] = input_limbs[i];
|
||||
}
|
||||
|
||||
for (j = 0; j < 2; ++j) {
|
||||
for (i = 0; i < 9; ++i) {
|
||||
if ((i & 1) == 1) {
|
||||
/* This calculation is a time-invariant way to make input[i]
|
||||
* non-negative by borrowing from the next-larger limb. */
|
||||
const s32 mask = input[i] >> 31;
|
||||
const s32 carry = -((input[i] & mask) >> 25);
|
||||
input[i] = input[i] + (carry << 25);
|
||||
input[i+1] = input[i+1] - carry;
|
||||
} else {
|
||||
const s32 mask = input[i] >> 31;
|
||||
const s32 carry = -((input[i] & mask) >> 26);
|
||||
input[i] = input[i] + (carry << 26);
|
||||
input[i+1] = input[i+1] - carry;
|
||||
}
|
||||
}
|
||||
|
||||
/* There's no greater limb for input[9] to borrow from, but we can multiply
|
||||
* by 19 and borrow from input[0], which is valid mod 2^255-19. */
|
||||
{
|
||||
const s32 mask = input[9] >> 31;
|
||||
const s32 carry = -((input[9] & mask) >> 25);
|
||||
input[9] = input[9] + (carry << 25);
|
||||
input[0] = input[0] - (carry * 19);
|
||||
}
|
||||
|
||||
/* After the first iteration, input[1..9] are non-negative and fit within
|
||||
* 25 or 26 bits, depending on position. However, input[0] may be
|
||||
* negative. */
|
||||
}
|
||||
|
||||
/* The first borrow-propagation pass above ended with every limb
|
||||
except (possibly) input[0] non-negative.
|
||||
|
||||
If input[0] was negative after the first pass, then it was because of a
|
||||
carry from input[9]. On entry, input[9] < 2^26 so the carry was, at most,
|
||||
one, since (2**26-1) >> 25 = 1. Thus input[0] >= -19.
|
||||
|
||||
In the second pass, each limb is decreased by at most one. Thus the second
|
||||
borrow-propagation pass could only have wrapped around to decrease
|
||||
input[0] again if the first pass left input[0] negative *and* input[1]
|
||||
through input[9] were all zero. In that case, input[1] is now 2^25 - 1,
|
||||
and this last borrow-propagation step will leave input[1] non-negative. */
|
||||
{
|
||||
const s32 mask = input[0] >> 31;
|
||||
const s32 carry = -((input[0] & mask) >> 26);
|
||||
input[0] = input[0] + (carry << 26);
|
||||
input[1] = input[1] - carry;
|
||||
}
|
||||
|
||||
/* All input[i] are now non-negative. However, there might be values between
|
||||
* 2^25 and 2^26 in a limb which is, nominally, 25 bits wide. */
|
||||
for (j = 0; j < 2; j++) {
|
||||
for (i = 0; i < 9; i++) {
|
||||
if ((i & 1) == 1) {
|
||||
const s32 carry = input[i] >> 25;
|
||||
input[i] &= 0x1ffffff;
|
||||
input[i+1] += carry;
|
||||
} else {
|
||||
const s32 carry = input[i] >> 26;
|
||||
input[i] &= 0x3ffffff;
|
||||
input[i+1] += carry;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const s32 carry = input[9] >> 25;
|
||||
input[9] &= 0x1ffffff;
|
||||
input[0] += 19*carry;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the first carry-chain pass, just above, ended up with a carry from
|
||||
* input[9], and that caused input[0] to be out-of-bounds, then input[0] was
|
||||
* < 2^26 + 2*19, because the carry was, at most, two.
|
||||
*
|
||||
* If the second pass carried from input[9] again then input[0] is < 2*19 and
|
||||
* the input[9] -> input[0] carry didn't push input[0] out of bounds. */
|
||||
|
||||
/* It still remains the case that input might be between 2^255-19 and 2^255.
|
||||
* In this case, input[1..9] must take their maximum value and input[0] must
|
||||
* be >= (2^255-19) & 0x3ffffff, which is 0x3ffffed. */
|
||||
mask = s32_gte(input[0], 0x3ffffed);
|
||||
for (i = 1; i < 10; i++) {
|
||||
if ((i & 1) == 1) {
|
||||
mask &= s32_eq(input[i], 0x1ffffff);
|
||||
} else {
|
||||
mask &= s32_eq(input[i], 0x3ffffff);
|
||||
}
|
||||
}
|
||||
|
||||
/* mask is either 0xffffffff (if input >= 2^255-19) and zero otherwise. Thus
|
||||
* this conditionally subtracts 2^255-19. */
|
||||
input[0] -= mask & 0x3ffffed;
|
||||
|
||||
for (i = 1; i < 10; i++) {
|
||||
if ((i & 1) == 1) {
|
||||
input[i] -= mask & 0x1ffffff;
|
||||
} else {
|
||||
input[i] -= mask & 0x3ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
input[1] <<= 2;
|
||||
input[2] <<= 3;
|
||||
input[3] <<= 5;
|
||||
input[4] <<= 6;
|
||||
input[6] <<= 1;
|
||||
input[7] <<= 3;
|
||||
input[8] <<= 4;
|
||||
input[9] <<= 6;
|
||||
#define F(i, s) \
|
||||
output[s+0] |= input[i] & 0xff; \
|
||||
output[s+1] = (input[i] >> 8) & 0xff; \
|
||||
output[s+2] = (input[i] >> 16) & 0xff; \
|
||||
output[s+3] = (input[i] >> 24) & 0xff;
|
||||
output[0] = 0;
|
||||
output[16] = 0;
|
||||
F(0,0);
|
||||
F(1,3);
|
||||
F(2,6);
|
||||
F(3,9);
|
||||
F(4,12);
|
||||
F(5,16);
|
||||
F(6,19);
|
||||
F(7,22);
|
||||
F(8,25);
|
||||
F(9,28);
|
||||
#undef F
|
||||
}
|
||||
|
||||
/* Input: Q, Q', Q-Q'
|
||||
* Output: 2Q, Q+Q'
|
||||
*
|
||||
* x2 z3: long form
|
||||
* x3 z3: long form
|
||||
* x z: short form, destroyed
|
||||
* xprime zprime: short form, destroyed
|
||||
* qmqp: short form, preserved
|
||||
*
|
||||
* On entry and exit, the absolute value of the limbs of all inputs and outputs
|
||||
* are < 2^26. */
|
||||
static void fmonty(limb *x2, limb *z2, /* output 2Q */
|
||||
limb *x3, limb *z3, /* output Q + Q' */
|
||||
limb *x, limb *z, /* input Q */
|
||||
limb *xprime, limb *zprime, /* input Q' */
|
||||
const limb *qmqp /* input Q - Q' */) {
|
||||
limb origx[10], origxprime[10], zzz[19], xx[19], zz[19], xxprime[19],
|
||||
zzprime[19], zzzprime[19], xxxprime[19];
|
||||
|
||||
memcpy(origx, x, 10 * sizeof(limb));
|
||||
fsum(x, z);
|
||||
/* |x[i]| < 2^27 */
|
||||
fdifference(z, origx); /* does x - z */
|
||||
/* |z[i]| < 2^27 */
|
||||
|
||||
memcpy(origxprime, xprime, sizeof(limb) * 10);
|
||||
fsum(xprime, zprime);
|
||||
/* |xprime[i]| < 2^27 */
|
||||
fdifference(zprime, origxprime);
|
||||
/* |zprime[i]| < 2^27 */
|
||||
fproduct(xxprime, xprime, z);
|
||||
/* |xxprime[i]| < 14*2^54: the largest product of two limbs will be <
|
||||
* 2^(27+27) and fproduct adds together, at most, 14 of those products.
|
||||
* (Approximating that to 2^58 doesn't work out.) */
|
||||
fproduct(zzprime, x, zprime);
|
||||
/* |zzprime[i]| < 14*2^54 */
|
||||
freduce_degree(xxprime);
|
||||
freduce_coefficients(xxprime);
|
||||
/* |xxprime[i]| < 2^26 */
|
||||
freduce_degree(zzprime);
|
||||
freduce_coefficients(zzprime);
|
||||
/* |zzprime[i]| < 2^26 */
|
||||
memcpy(origxprime, xxprime, sizeof(limb) * 10);
|
||||
fsum(xxprime, zzprime);
|
||||
/* |xxprime[i]| < 2^27 */
|
||||
fdifference(zzprime, origxprime);
|
||||
/* |zzprime[i]| < 2^27 */
|
||||
fsquare(xxxprime, xxprime);
|
||||
/* |xxxprime[i]| < 2^26 */
|
||||
fsquare(zzzprime, zzprime);
|
||||
/* |zzzprime[i]| < 2^26 */
|
||||
fproduct(zzprime, zzzprime, qmqp);
|
||||
/* |zzprime[i]| < 14*2^52 */
|
||||
freduce_degree(zzprime);
|
||||
freduce_coefficients(zzprime);
|
||||
/* |zzprime[i]| < 2^26 */
|
||||
memcpy(x3, xxxprime, sizeof(limb) * 10);
|
||||
memcpy(z3, zzprime, sizeof(limb) * 10);
|
||||
|
||||
fsquare(xx, x);
|
||||
/* |xx[i]| < 2^26 */
|
||||
fsquare(zz, z);
|
||||
/* |zz[i]| < 2^26 */
|
||||
fproduct(x2, xx, zz);
|
||||
/* |x2[i]| < 14*2^52 */
|
||||
freduce_degree(x2);
|
||||
freduce_coefficients(x2);
|
||||
/* |x2[i]| < 2^26 */
|
||||
fdifference(zz, xx); // does zz = xx - zz
|
||||
/* |zz[i]| < 2^27 */
|
||||
memset(zzz + 10, 0, sizeof(limb) * 9);
|
||||
fscalar_product(zzz, zz, 121665);
|
||||
/* |zzz[i]| < 2^(27+17) */
|
||||
/* No need to call freduce_degree here:
|
||||
fscalar_product doesn't increase the degree of its input. */
|
||||
freduce_coefficients(zzz);
|
||||
/* |zzz[i]| < 2^26 */
|
||||
fsum(zzz, xx);
|
||||
/* |zzz[i]| < 2^27 */
|
||||
fproduct(z2, zz, zzz);
|
||||
/* |z2[i]| < 14*2^(26+27) */
|
||||
freduce_degree(z2);
|
||||
freduce_coefficients(z2);
|
||||
/* |z2|i| < 2^26 */
|
||||
}
|
||||
|
||||
/* Conditionally swap two reduced-form limb arrays if 'iswap' is 1, but leave
|
||||
* them unchanged if 'iswap' is 0. Runs in data-invariant time to avoid
|
||||
* side-channel attacks.
|
||||
*
|
||||
* NOTE that this function requires that 'iswap' be 1 or 0; other values give
|
||||
* wrong results. Also, the two limb arrays must be in reduced-coefficient,
|
||||
* reduced-degree form: the values in a[10..19] or b[10..19] aren't swapped,
|
||||
* and all all values in a[0..9],b[0..9] must have magnitude less than
|
||||
* INT32_MAX. */
|
||||
static void
|
||||
swap_conditional(limb a[19], limb b[19], limb iswap) {
|
||||
unsigned i;
|
||||
const s32 swap = (s32) -iswap;
|
||||
|
||||
for (i = 0; i < 10; ++i) {
|
||||
const s32 x = swap & ( ((s32)a[i]) ^ ((s32)b[i]) );
|
||||
a[i] = ((s32)a[i]) ^ x;
|
||||
b[i] = ((s32)b[i]) ^ x;
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculates nQ where Q is the x-coordinate of a point on the curve
|
||||
*
|
||||
* resultx/resultz: the x coordinate of the resulting curve point (short form)
|
||||
* n: a little endian, 32-byte number
|
||||
* q: a point of the curve (short form) */
|
||||
static void
|
||||
cmult(limb *resultx, limb *resultz, const u8 *n, const limb *q) {
|
||||
limb a[19] = {0}, b[19] = {1}, c[19] = {1}, d[19] = {0};
|
||||
limb *nqpqx = a, *nqpqz = b, *nqx = c, *nqz = d, *t;
|
||||
limb e[19] = {0}, f[19] = {1}, g[19] = {0}, h[19] = {1};
|
||||
limb *nqpqx2 = e, *nqpqz2 = f, *nqx2 = g, *nqz2 = h;
|
||||
|
||||
unsigned i, j;
|
||||
|
||||
memcpy(nqpqx, q, sizeof(limb) * 10);
|
||||
|
||||
for (i = 0; i < 32; ++i) {
|
||||
u8 byte = n[31 - i];
|
||||
for (j = 0; j < 8; ++j) {
|
||||
const limb bit = byte >> 7;
|
||||
|
||||
swap_conditional(nqx, nqpqx, bit);
|
||||
swap_conditional(nqz, nqpqz, bit);
|
||||
fmonty(nqx2, nqz2,
|
||||
nqpqx2, nqpqz2,
|
||||
nqx, nqz,
|
||||
nqpqx, nqpqz,
|
||||
q);
|
||||
swap_conditional(nqx2, nqpqx2, bit);
|
||||
swap_conditional(nqz2, nqpqz2, bit);
|
||||
|
||||
t = nqx;
|
||||
nqx = nqx2;
|
||||
nqx2 = t;
|
||||
t = nqz;
|
||||
nqz = nqz2;
|
||||
nqz2 = t;
|
||||
t = nqpqx;
|
||||
nqpqx = nqpqx2;
|
||||
nqpqx2 = t;
|
||||
t = nqpqz;
|
||||
nqpqz = nqpqz2;
|
||||
nqpqz2 = t;
|
||||
|
||||
byte <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(resultx, nqx, sizeof(limb) * 10);
|
||||
memcpy(resultz, nqz, sizeof(limb) * 10);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Shamelessly copied from djb's code
|
||||
// -----------------------------------------------------------------------------
|
||||
static void
|
||||
crecip(limb *out, const limb *z) {
|
||||
limb z2[10];
|
||||
limb z9[10];
|
||||
limb z11[10];
|
||||
limb z2_5_0[10];
|
||||
limb z2_10_0[10];
|
||||
limb z2_20_0[10];
|
||||
limb z2_50_0[10];
|
||||
limb z2_100_0[10];
|
||||
limb t0[10];
|
||||
limb t1[10];
|
||||
int i;
|
||||
|
||||
/* 2 */ fsquare(z2,z);
|
||||
/* 4 */ fsquare(t1,z2);
|
||||
/* 8 */ fsquare(t0,t1);
|
||||
/* 9 */ fmul(z9,t0,z);
|
||||
/* 11 */ fmul(z11,z9,z2);
|
||||
/* 22 */ fsquare(t0,z11);
|
||||
/* 2^5 - 2^0 = 31 */ fmul(z2_5_0,t0,z9);
|
||||
|
||||
/* 2^6 - 2^1 */ fsquare(t0,z2_5_0);
|
||||
/* 2^7 - 2^2 */ fsquare(t1,t0);
|
||||
/* 2^8 - 2^3 */ fsquare(t0,t1);
|
||||
/* 2^9 - 2^4 */ fsquare(t1,t0);
|
||||
/* 2^10 - 2^5 */ fsquare(t0,t1);
|
||||
/* 2^10 - 2^0 */ fmul(z2_10_0,t0,z2_5_0);
|
||||
|
||||
/* 2^11 - 2^1 */ fsquare(t0,z2_10_0);
|
||||
/* 2^12 - 2^2 */ fsquare(t1,t0);
|
||||
/* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
|
||||
/* 2^20 - 2^0 */ fmul(z2_20_0,t1,z2_10_0);
|
||||
|
||||
/* 2^21 - 2^1 */ fsquare(t0,z2_20_0);
|
||||
/* 2^22 - 2^2 */ fsquare(t1,t0);
|
||||
/* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
|
||||
/* 2^40 - 2^0 */ fmul(t0,t1,z2_20_0);
|
||||
|
||||
/* 2^41 - 2^1 */ fsquare(t1,t0);
|
||||
/* 2^42 - 2^2 */ fsquare(t0,t1);
|
||||
/* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t1,t0); fsquare(t0,t1); }
|
||||
/* 2^50 - 2^0 */ fmul(z2_50_0,t0,z2_10_0);
|
||||
|
||||
/* 2^51 - 2^1 */ fsquare(t0,z2_50_0);
|
||||
/* 2^52 - 2^2 */ fsquare(t1,t0);
|
||||
/* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
|
||||
/* 2^100 - 2^0 */ fmul(z2_100_0,t1,z2_50_0);
|
||||
|
||||
/* 2^101 - 2^1 */ fsquare(t1,z2_100_0);
|
||||
/* 2^102 - 2^2 */ fsquare(t0,t1);
|
||||
/* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { fsquare(t1,t0); fsquare(t0,t1); }
|
||||
/* 2^200 - 2^0 */ fmul(t1,t0,z2_100_0);
|
||||
|
||||
/* 2^201 - 2^1 */ fsquare(t0,t1);
|
||||
/* 2^202 - 2^2 */ fsquare(t1,t0);
|
||||
/* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
|
||||
/* 2^250 - 2^0 */ fmul(t0,t1,z2_50_0);
|
||||
|
||||
/* 2^251 - 2^1 */ fsquare(t1,t0);
|
||||
/* 2^252 - 2^2 */ fsquare(t0,t1);
|
||||
/* 2^253 - 2^3 */ fsquare(t1,t0);
|
||||
/* 2^254 - 2^4 */ fsquare(t0,t1);
|
||||
/* 2^255 - 2^5 */ fsquare(t1,t0);
|
||||
/* 2^255 - 21 */ fmul(out,t1,z11);
|
||||
}
|
||||
|
||||
int
|
||||
curve25519_donna(u8 *mypublic, const u8 *secret, const u8 *basepoint) {
|
||||
limb bp[10], x[10], z[11], zmone[10];
|
||||
uint8_t e[32];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 32; ++i) e[i] = secret[i];
|
||||
e[0] &= 248;
|
||||
e[31] &= 127;
|
||||
e[31] |= 64;
|
||||
|
||||
fexpand(bp, basepoint);
|
||||
cmult(x, z, e, bp);
|
||||
crecip(zmone, z);
|
||||
fmul(z, x, zmone);
|
||||
fcontract(mypublic, z);
|
||||
return 0;
|
||||
}
|
||||
8
lib/curve25519/curve25519.h
Executable file
8
lib/curve25519/curve25519.h
Executable file
@@ -0,0 +1,8 @@
|
||||
#ifndef CURVE25519_DONNA_H
|
||||
#define CURVE25519_DONNA_H
|
||||
|
||||
static const unsigned char kCurve25519BasePoint[32] = { 9 };
|
||||
|
||||
int curve25519_donna(unsigned char *mypublic, const unsigned char *secret, const unsigned char *basepoint);
|
||||
|
||||
#endif
|
||||
406
lib/dnssd.c
Normal file
406
lib/dnssd.c
Normal file
@@ -0,0 +1,406 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* These defines allow us to compile on iOS */
|
||||
#ifndef __has_feature
|
||||
# define __has_feature(x) 0
|
||||
#endif
|
||||
#ifndef __has_extension
|
||||
# define __has_extension __has_feature
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "dnssdint.h"
|
||||
#include "dnssd.h"
|
||||
#include "global.h"
|
||||
#include "compat.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <dns_sd.h>
|
||||
|
||||
#define MAX_DEVICEID 18
|
||||
#define MAX_SERVNAME 256
|
||||
|
||||
#if defined(HAVE_LIBDL) && !defined(__APPLE__)
|
||||
# define USE_LIBDL 1
|
||||
#else
|
||||
# define USE_LIBDL 0
|
||||
#endif
|
||||
|
||||
#if defined(WIN32) || USE_LIBDL
|
||||
# ifdef WIN32
|
||||
# include <stdint.h>
|
||||
# if !defined(EFI32) && !defined(EFI64)
|
||||
# define DNSSD_STDCALL __stdcall
|
||||
# else
|
||||
# define DNSSD_STDCALL
|
||||
# endif
|
||||
# else
|
||||
# include <dlfcn.h>
|
||||
# define DNSSD_STDCALL
|
||||
# endif
|
||||
|
||||
typedef struct _DNSServiceRef_t *DNSServiceRef;
|
||||
typedef union _TXTRecordRef_t { char PrivateData[16]; char *ForceNaturalAlignment; } TXTRecordRef;
|
||||
|
||||
typedef uint32_t DNSServiceFlags;
|
||||
typedef int32_t DNSServiceErrorType;
|
||||
|
||||
typedef void (DNSSD_STDCALL *DNSServiceRegisterReply)
|
||||
(
|
||||
DNSServiceRef sdRef,
|
||||
DNSServiceFlags flags,
|
||||
DNSServiceErrorType errorCode,
|
||||
const char *name,
|
||||
const char *regtype,
|
||||
const char *domain,
|
||||
void *context
|
||||
);
|
||||
|
||||
#else
|
||||
//# include <dns_sd.h>
|
||||
# define DNSSD_STDCALL
|
||||
#endif
|
||||
|
||||
typedef DNSServiceErrorType (DNSSD_STDCALL *DNSServiceRegister_t)
|
||||
(
|
||||
DNSServiceRef *sdRef,
|
||||
DNSServiceFlags flags,
|
||||
uint32_t interfaceIndex,
|
||||
const char *name,
|
||||
const char *regtype,
|
||||
const char *domain,
|
||||
const char *host,
|
||||
uint16_t port,
|
||||
uint16_t txtLen,
|
||||
const void *txtRecord,
|
||||
DNSServiceRegisterReply callBack,
|
||||
void *context
|
||||
);
|
||||
typedef void (DNSSD_STDCALL *DNSServiceRefDeallocate_t)(DNSServiceRef sdRef);
|
||||
typedef void (DNSSD_STDCALL *TXTRecordCreate_t)
|
||||
(
|
||||
TXTRecordRef *txtRecord,
|
||||
uint16_t bufferLen,
|
||||
void *buffer
|
||||
);
|
||||
typedef void (DNSSD_STDCALL *TXTRecordDeallocate_t)(TXTRecordRef *txtRecord);
|
||||
typedef DNSServiceErrorType (DNSSD_STDCALL *TXTRecordSetValue_t)
|
||||
(
|
||||
TXTRecordRef *txtRecord,
|
||||
const char *key,
|
||||
uint8_t valueSize,
|
||||
const void *value
|
||||
);
|
||||
typedef uint16_t (DNSSD_STDCALL *TXTRecordGetLength_t)(const TXTRecordRef *txtRecord);
|
||||
typedef const void * (DNSSD_STDCALL *TXTRecordGetBytesPtr_t)(const TXTRecordRef *txtRecord);
|
||||
|
||||
|
||||
struct dnssd_s {
|
||||
#ifdef WIN32
|
||||
HMODULE module;
|
||||
#elif USE_LIBDL
|
||||
void *module;
|
||||
#endif
|
||||
|
||||
DNSServiceRegister_t DNSServiceRegister;
|
||||
DNSServiceRefDeallocate_t DNSServiceRefDeallocate;
|
||||
TXTRecordCreate_t TXTRecordCreate;
|
||||
TXTRecordSetValue_t TXTRecordSetValue;
|
||||
TXTRecordGetLength_t TXTRecordGetLength;
|
||||
TXTRecordGetBytesPtr_t TXTRecordGetBytesPtr;
|
||||
TXTRecordDeallocate_t TXTRecordDeallocate;
|
||||
|
||||
TXTRecordRef raop_record;
|
||||
TXTRecordRef airplay_record;
|
||||
|
||||
DNSServiceRef raop_service;
|
||||
DNSServiceRef airplay_service;
|
||||
|
||||
char *name;
|
||||
int name_len;
|
||||
|
||||
char *hw_addr;
|
||||
int hw_addr_len;
|
||||
};
|
||||
|
||||
|
||||
|
||||
dnssd_t *
|
||||
dnssd_init(const char* name, int name_len, const char* hw_addr, int hw_addr_len, int *error)
|
||||
{
|
||||
dnssd_t *dnssd;
|
||||
|
||||
if (error) *error = DNSSD_ERROR_NOERROR;
|
||||
|
||||
dnssd = calloc(1, sizeof(dnssd_t));
|
||||
if (!dnssd) {
|
||||
if (error) *error = DNSSD_ERROR_OUTOFMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
dnssd->module = LoadLibraryA("dnssd.dll");
|
||||
if (!dnssd->module) {
|
||||
if (error) *error = DNSSD_ERROR_LIBNOTFOUND;
|
||||
free(dnssd);
|
||||
return NULL;
|
||||
}
|
||||
dnssd->DNSServiceRegister = (DNSServiceRegister_t)GetProcAddress(dnssd->module, "DNSServiceRegister");
|
||||
dnssd->DNSServiceRefDeallocate = (DNSServiceRefDeallocate_t)GetProcAddress(dnssd->module, "DNSServiceRefDeallocate");
|
||||
dnssd->TXTRecordCreate = (TXTRecordCreate_t)GetProcAddress(dnssd->module, "TXTRecordCreate");
|
||||
dnssd->TXTRecordSetValue = (TXTRecordSetValue_t)GetProcAddress(dnssd->module, "TXTRecordSetValue");
|
||||
dnssd->TXTRecordGetLength = (TXTRecordGetLength_t)GetProcAddress(dnssd->module, "TXTRecordGetLength");
|
||||
dnssd->TXTRecordGetBytesPtr = (TXTRecordGetBytesPtr_t)GetProcAddress(dnssd->module, "TXTRecordGetBytesPtr");
|
||||
dnssd->TXTRecordDeallocate = (TXTRecordDeallocate_t)GetProcAddress(dnssd->module, "TXTRecordDeallocate");
|
||||
|
||||
if (!dnssd->DNSServiceRegister || !dnssd->DNSServiceRefDeallocate || !dnssd->TXTRecordCreate ||
|
||||
!dnssd->TXTRecordSetValue || !dnssd->TXTRecordGetLength || !dnssd->TXTRecordGetBytesPtr ||
|
||||
!dnssd->TXTRecordDeallocate) {
|
||||
if (error) *error = DNSSD_ERROR_PROCNOTFOUND;
|
||||
FreeLibrary(dnssd->module);
|
||||
free(dnssd);
|
||||
return NULL;
|
||||
}
|
||||
#elif USE_LIBDL
|
||||
dnssd->module = dlopen("libdns_sd.so", RTLD_LAZY);
|
||||
if (!dnssd->module) {
|
||||
if (error) *error = DNSSD_ERROR_LIBNOTFOUND;
|
||||
free(dnssd);
|
||||
return NULL;
|
||||
}
|
||||
dnssd->DNSServiceRegister = (DNSServiceRegister_t)dlsym(dnssd->module, "DNSServiceRegister");
|
||||
dnssd->DNSServiceRefDeallocate = (DNSServiceRefDeallocate_t)dlsym(dnssd->module, "DNSServiceRefDeallocate");
|
||||
dnssd->TXTRecordCreate = (TXTRecordCreate_t)dlsym(dnssd->module, "TXTRecordCreate");
|
||||
dnssd->TXTRecordSetValue = (TXTRecordSetValue_t)dlsym(dnssd->module, "TXTRecordSetValue");
|
||||
dnssd->TXTRecordGetLength = (TXTRecordGetLength_t)dlsym(dnssd->module, "TXTRecordGetLength");
|
||||
dnssd->TXTRecordGetBytesPtr = (TXTRecordGetBytesPtr_t)dlsym(dnssd->module, "TXTRecordGetBytesPtr");
|
||||
dnssd->TXTRecordDeallocate = (TXTRecordDeallocate_t)dlsym(dnssd->module, "TXTRecordDeallocate");
|
||||
|
||||
if (!dnssd->DNSServiceRegister || !dnssd->DNSServiceRefDeallocate || !dnssd->TXTRecordCreate ||
|
||||
!dnssd->TXTRecordSetValue || !dnssd->TXTRecordGetLength || !dnssd->TXTRecordGetBytesPtr ||
|
||||
!dnssd->TXTRecordDeallocate) {
|
||||
if (error) *error = DNSSD_ERROR_PROCNOTFOUND;
|
||||
dlclose(dnssd->module);
|
||||
free(dnssd);
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
dnssd->DNSServiceRegister = &DNSServiceRegister;
|
||||
dnssd->DNSServiceRefDeallocate = &DNSServiceRefDeallocate;
|
||||
dnssd->TXTRecordCreate = &TXTRecordCreate;
|
||||
dnssd->TXTRecordSetValue = &TXTRecordSetValue;
|
||||
dnssd->TXTRecordGetLength = &TXTRecordGetLength;
|
||||
dnssd->TXTRecordGetBytesPtr = &TXTRecordGetBytesPtr;
|
||||
dnssd->TXTRecordDeallocate = &TXTRecordDeallocate;
|
||||
#endif
|
||||
|
||||
dnssd->name_len = name_len;
|
||||
dnssd->name = calloc(1, name_len);
|
||||
if (!dnssd->name) {
|
||||
free(dnssd);
|
||||
if (error) *error = DNSSD_ERROR_OUTOFMEM;
|
||||
return NULL;
|
||||
}
|
||||
memcpy(dnssd->name, name, name_len);
|
||||
|
||||
dnssd->hw_addr_len = hw_addr_len;
|
||||
dnssd->hw_addr = calloc(1, dnssd->hw_addr_len);
|
||||
if (!dnssd->hw_addr) {
|
||||
free(dnssd->name);
|
||||
free(dnssd);
|
||||
if (error) *error = DNSSD_ERROR_OUTOFMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(dnssd->hw_addr, hw_addr, hw_addr_len);
|
||||
|
||||
return dnssd;
|
||||
}
|
||||
|
||||
void
|
||||
dnssd_destroy(dnssd_t *dnssd)
|
||||
{
|
||||
if (dnssd) {
|
||||
#ifdef WIN32
|
||||
FreeLibrary(dnssd->module);
|
||||
#elif USE_LIBDL
|
||||
dlclose(dnssd->module);
|
||||
#endif
|
||||
free(dnssd);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
dnssd_register_raop(dnssd_t *dnssd, unsigned short port)
|
||||
{
|
||||
char servname[MAX_SERVNAME];
|
||||
|
||||
assert(dnssd);
|
||||
|
||||
dnssd->TXTRecordCreate(&dnssd->raop_record, 0, NULL);
|
||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "ch", strlen(RAOP_CH), RAOP_CH);
|
||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "cn", strlen(RAOP_CN), RAOP_CN);
|
||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "da", strlen(RAOP_DA), RAOP_DA);
|
||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "et", strlen(RAOP_ET), RAOP_ET);
|
||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "vv", strlen(RAOP_VV), RAOP_VV);
|
||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "ft", strlen(RAOP_FT), RAOP_FT);
|
||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "am", strlen(GLOBAL_MODEL), GLOBAL_MODEL);
|
||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "md", strlen(RAOP_MD), RAOP_MD);
|
||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "rhd", strlen(RAOP_RHD), RAOP_RHD);
|
||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "pw", strlen("false"), "false");
|
||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "sr", strlen(RAOP_SR), RAOP_SR);
|
||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "ss", strlen(RAOP_SS), RAOP_SS);
|
||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "sv", strlen(RAOP_SV), RAOP_SV);
|
||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "tp", strlen(RAOP_TP), RAOP_TP);
|
||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "txtvers", strlen(RAOP_TXTVERS), RAOP_TXTVERS);
|
||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "sf", strlen(RAOP_SF), RAOP_SF);
|
||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "vs", strlen(RAOP_VS), RAOP_VS);
|
||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "vn", strlen(RAOP_VN), RAOP_VN);
|
||||
dnssd->TXTRecordSetValue(&dnssd->raop_record, "pk", strlen(RAOP_PK), RAOP_PK);
|
||||
|
||||
/* Convert hardware address to string */
|
||||
if (utils_hwaddr_raop(servname, sizeof(servname), dnssd->hw_addr, dnssd->hw_addr_len) < 0) {
|
||||
/* FIXME: handle better */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check that we have bytes for 'hw@name' format */
|
||||
if (sizeof(servname) < strlen(servname) + 1 + dnssd->name_len + 1) {
|
||||
/* FIXME: handle better */
|
||||
return -2;
|
||||
}
|
||||
|
||||
strncat(servname, "@", sizeof(servname)-strlen(servname)-1);
|
||||
strncat(servname, dnssd->name, sizeof(servname)-strlen(servname)-1);
|
||||
|
||||
/* Register the service */
|
||||
dnssd->DNSServiceRegister(&dnssd->raop_service, 0, 0,
|
||||
servname, "_raop._tcp",
|
||||
NULL, NULL,
|
||||
htons(port),
|
||||
dnssd->TXTRecordGetLength(&dnssd->raop_record),
|
||||
dnssd->TXTRecordGetBytesPtr(&dnssd->raop_record),
|
||||
NULL, NULL);
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
dnssd_register_airplay(dnssd_t *dnssd, unsigned short port)
|
||||
{
|
||||
char device_id[3 * MAX_HWADDR_LEN];
|
||||
|
||||
assert(dnssd);
|
||||
|
||||
/* Convert hardware address to string */
|
||||
if (utils_hwaddr_airplay(device_id, sizeof(device_id), dnssd->hw_addr, dnssd->hw_addr_len) < 0) {
|
||||
/* FIXME: handle better */
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
dnssd->TXTRecordCreate(&dnssd->airplay_record, 0, NULL);
|
||||
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "deviceid", strlen(device_id), device_id);
|
||||
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "features", strlen(AIRPLAY_FEATURES), AIRPLAY_FEATURES);
|
||||
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "flags", strlen(AIRPLAY_FLAGS), AIRPLAY_FLAGS);
|
||||
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "model", strlen(GLOBAL_MODEL), GLOBAL_MODEL);
|
||||
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "pk", strlen(AIRPLAY_PK), AIRPLAY_PK);
|
||||
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "pi", strlen(AIRPLAY_PI), AIRPLAY_PI);
|
||||
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "srcvers", strlen(AIRPLAY_SRCVERS), AIRPLAY_SRCVERS);
|
||||
dnssd->TXTRecordSetValue(&dnssd->airplay_record, "vv", strlen(AIRPLAY_VV), AIRPLAY_VV);
|
||||
|
||||
/* Register the service */
|
||||
dnssd->DNSServiceRegister(&dnssd->airplay_service, 0, 0,
|
||||
dnssd->name, "_airplay._tcp",
|
||||
NULL, NULL,
|
||||
htons(port),
|
||||
dnssd->TXTRecordGetLength(&dnssd->airplay_record),
|
||||
dnssd->TXTRecordGetBytesPtr(&dnssd->airplay_record),
|
||||
NULL, NULL);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *
|
||||
dnssd_get_airplay_txt(dnssd_t *dnssd, int *length)
|
||||
{
|
||||
*length = dnssd->TXTRecordGetLength(&dnssd->airplay_record);
|
||||
return dnssd->TXTRecordGetBytesPtr(&dnssd->airplay_record);
|
||||
}
|
||||
|
||||
const char *
|
||||
dnssd_get_name(dnssd_t *dnssd, int *length)
|
||||
{
|
||||
*length = dnssd->name_len;
|
||||
return dnssd->name;
|
||||
}
|
||||
|
||||
const char *
|
||||
dnssd_get_hw_addr(dnssd_t *dnssd, int *length)
|
||||
{
|
||||
*length = dnssd->hw_addr_len;
|
||||
return dnssd->hw_addr;
|
||||
}
|
||||
|
||||
void
|
||||
dnssd_unregister_raop(dnssd_t *dnssd)
|
||||
{
|
||||
assert(dnssd);
|
||||
|
||||
if (!dnssd->raop_service) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Deallocate TXT record */
|
||||
dnssd->TXTRecordDeallocate(&dnssd->raop_record);
|
||||
|
||||
dnssd->DNSServiceRefDeallocate(dnssd->raop_service);
|
||||
dnssd->raop_service = NULL;
|
||||
|
||||
if (dnssd->airplay_service == NULL) {
|
||||
free(dnssd->name);
|
||||
free(dnssd->hw_addr);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
dnssd_unregister_airplay(dnssd_t *dnssd)
|
||||
{
|
||||
assert(dnssd);
|
||||
|
||||
if (!dnssd->airplay_service) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Deallocate TXT record */
|
||||
dnssd->TXTRecordDeallocate(&dnssd->airplay_record);
|
||||
|
||||
dnssd->DNSServiceRefDeallocate(dnssd->airplay_service);
|
||||
dnssd->airplay_service = NULL;
|
||||
|
||||
if (dnssd->raop_service == NULL) {
|
||||
free(dnssd->name);
|
||||
free(dnssd->hw_addr);
|
||||
}
|
||||
}
|
||||
39
lib/dnssd.h
Normal file
39
lib/dnssd.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef DNSSD_H
|
||||
#define DNSSD_H
|
||||
|
||||
#if defined(WIN32) && defined(DLL_EXPORT)
|
||||
# define DNSSD_API __declspec(dllexport)
|
||||
#else
|
||||
# define DNSSD_API
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define DNSSD_ERROR_NOERROR 0
|
||||
#define DNSSD_ERROR_HWADDRLEN 1
|
||||
#define DNSSD_ERROR_OUTOFMEM 2
|
||||
#define DNSSD_ERROR_LIBNOTFOUND 3
|
||||
#define DNSSD_ERROR_PROCNOTFOUND 4
|
||||
|
||||
typedef struct dnssd_s dnssd_t;
|
||||
|
||||
DNSSD_API dnssd_t *dnssd_init(const char *name, int name_len, const char *hw_addr, int hw_addr_len, int *error);
|
||||
|
||||
DNSSD_API int dnssd_register_raop(dnssd_t *dnssd, unsigned short port);
|
||||
DNSSD_API int dnssd_register_airplay(dnssd_t *dnssd, unsigned short port);
|
||||
|
||||
DNSSD_API void dnssd_unregister_raop(dnssd_t *dnssd);
|
||||
DNSSD_API void dnssd_unregister_airplay(dnssd_t *dnssd);
|
||||
|
||||
DNSSD_API const char *dnssd_get_airplay_txt(dnssd_t *dnssd, int *length);
|
||||
DNSSD_API const char *dnssd_get_name(dnssd_t *dnssd, int *length);
|
||||
DNSSD_API const char *dnssd_get_hw_addr(dnssd_t *dnssd, int *length);
|
||||
|
||||
DNSSD_API void dnssd_destroy(dnssd_t *dnssd);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
29
lib/dnssdint.h
Normal file
29
lib/dnssdint.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef DNSSDINT_H
|
||||
#define DNSSDINT_H
|
||||
|
||||
#define RAOP_TXTVERS "1"
|
||||
#define RAOP_CH "2" /* Audio channels: 2 */
|
||||
#define RAOP_CN "0,1,2,3" /* Audio codec: PCM, ALAC, AAC, AAC ELD */
|
||||
#define RAOP_ET "0,3,5" /* Encryption type: None, FairPlay, FairPlay SAPv2.5 */
|
||||
#define RAOP_VV "2"
|
||||
#define RAOP_FT "0x5A7FFFF7,0x1E"
|
||||
#define RAOP_RHD "5.6.0.0"
|
||||
#define RAOP_SF "0x4"
|
||||
#define RAOP_SV "false"
|
||||
#define RAOP_DA "true"
|
||||
#define RAOP_SR "44100" /* Sample rate: 44100 */
|
||||
#define RAOP_SS "16" /* Sample size: 16 */
|
||||
#define RAOP_VS "220.68"
|
||||
#define RAOP_TP "UDP" /* Transport protocol. Possible values: UDP or TCP or TCP,UDP */
|
||||
#define RAOP_MD "0,1,2" /* Metadata: text, artwork, progress */
|
||||
#define RAOP_VN "65537"
|
||||
#define RAOP_PK "b07727d6f6cd6e08b58ede525ec3cdeaa252ad9f683feb212ef8a205246554e7"
|
||||
|
||||
#define AIRPLAY_FEATURES "0x5A7FFFF7,0x1E"
|
||||
#define AIRPLAY_SRCVERS "220.68"
|
||||
#define AIRPLAY_FLAGS "0x4"
|
||||
#define AIRPLAY_VV "2"
|
||||
#define AIRPLAY_PK "b07727d6f6cd6e08b58ede525ec3cdeaa252ad9f683feb212ef8a205246554e7"
|
||||
#define AIRPLAY_PI "2e388006-13ba-4041-9a67-25dd4a43d536"
|
||||
|
||||
#endif
|
||||
6
lib/ed25519/CMakeLists.txt
Executable file
6
lib/ed25519/CMakeLists.txt
Executable file
@@ -0,0 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.4.1)
|
||||
aux_source_directory(. ed25519_src)
|
||||
set(DIR_SRCS ${ed25519_src})
|
||||
add_library( ed25519
|
||||
STATIC
|
||||
${DIR_SRCS})
|
||||
69
lib/ed25519/add_scalar.c
Executable file
69
lib/ed25519/add_scalar.c
Executable file
@@ -0,0 +1,69 @@
|
||||
#include "ed25519.h"
|
||||
#include "ge.h"
|
||||
#include "sc.h"
|
||||
#include "sha512.h"
|
||||
|
||||
|
||||
/* see http://crypto.stackexchange.com/a/6215/4697 */
|
||||
void ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar) {
|
||||
const unsigned char SC_1[32] = {1}; /* scalar with value 1 */
|
||||
|
||||
unsigned char n[32];
|
||||
ge_p3 nB;
|
||||
ge_p1p1 A_p1p1;
|
||||
ge_p3 A;
|
||||
ge_p3 public_key_unpacked;
|
||||
ge_cached T;
|
||||
|
||||
sha512_context hash;
|
||||
unsigned char hashbuf[64];
|
||||
|
||||
int i;
|
||||
|
||||
/* copy the scalar and clear highest bit */
|
||||
for (i = 0; i < 31; ++i) {
|
||||
n[i] = scalar[i];
|
||||
}
|
||||
n[31] = scalar[31] & 127;
|
||||
|
||||
/* private key: a = n + t */
|
||||
if (private_key) {
|
||||
sc_muladd(private_key, SC_1, n, private_key);
|
||||
|
||||
// https://github.com/orlp/ed25519/issues/3
|
||||
sha512_init(&hash);
|
||||
sha512_update(&hash, private_key + 32, 32);
|
||||
sha512_update(&hash, scalar, 32);
|
||||
sha512_final(&hash, hashbuf);
|
||||
for (i = 0; i < 32; ++i) {
|
||||
private_key[32 + i] = hashbuf[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* public key: A = nB + T */
|
||||
if (public_key) {
|
||||
/* if we know the private key we don't need a point addition, which is faster */
|
||||
/* using a "timing attack" you could find out wether or not we know the private
|
||||
key, but this information seems rather useless - if this is important pass
|
||||
public_key and private_key seperately in 2 function calls */
|
||||
if (private_key) {
|
||||
ge_scalarmult_base(&A, private_key);
|
||||
} else {
|
||||
/* unpack public key into T */
|
||||
ge_frombytes_negate_vartime(&public_key_unpacked, public_key);
|
||||
fe_neg(public_key_unpacked.X, public_key_unpacked.X); /* undo negate */
|
||||
fe_neg(public_key_unpacked.T, public_key_unpacked.T); /* undo negate */
|
||||
ge_p3_to_cached(&T, &public_key_unpacked);
|
||||
|
||||
/* calculate n*B */
|
||||
ge_scalarmult_base(&nB, n);
|
||||
|
||||
/* A = n*B + T */
|
||||
ge_add(&A_p1p1, &nB, &T);
|
||||
ge_p1p1_to_p3(&A, &A_p1p1);
|
||||
}
|
||||
|
||||
/* pack public key */
|
||||
ge_p3_tobytes(public_key, &A);
|
||||
}
|
||||
}
|
||||
38
lib/ed25519/ed25519.h
Executable file
38
lib/ed25519/ed25519.h
Executable file
@@ -0,0 +1,38 @@
|
||||
#ifndef ED25519_H
|
||||
#define ED25519_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#if defined(ED25519_BUILD_DLL)
|
||||
#define ED25519_DECLSPEC __declspec(dllexport)
|
||||
#elif defined(ED25519_DLL)
|
||||
#define ED25519_DECLSPEC __declspec(dllimport)
|
||||
#else
|
||||
#define ED25519_DECLSPEC
|
||||
#endif
|
||||
#else
|
||||
#define ED25519_DECLSPEC
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef ED25519_NO_SEED
|
||||
int ED25519_DECLSPEC ed25519_create_seed(unsigned char *seed);
|
||||
#endif
|
||||
|
||||
void ED25519_DECLSPEC ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed);
|
||||
void ED25519_DECLSPEC ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key);
|
||||
int ED25519_DECLSPEC ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key);
|
||||
void ED25519_DECLSPEC ed25519_add_scalar(unsigned char *public_key, unsigned char *private_key, const unsigned char *scalar);
|
||||
void ED25519_DECLSPEC ed25519_key_exchange(unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
1491
lib/ed25519/fe.c
Executable file
1491
lib/ed25519/fe.c
Executable file
File diff suppressed because it is too large
Load Diff
41
lib/ed25519/fe.h
Executable file
41
lib/ed25519/fe.h
Executable file
@@ -0,0 +1,41 @@
|
||||
#ifndef FE_H
|
||||
#define FE_H
|
||||
|
||||
#include "fixedint.h"
|
||||
|
||||
|
||||
/*
|
||||
fe means field element.
|
||||
Here the field is \Z/(2^255-19).
|
||||
An element t, entries t[0]...t[9], represents the integer
|
||||
t[0]+2^26 t[1]+2^51 t[2]+2^77 t[3]+2^102 t[4]+...+2^230 t[9].
|
||||
Bounds on each t[i] vary depending on context.
|
||||
*/
|
||||
|
||||
|
||||
typedef int32_t fe[10];
|
||||
|
||||
|
||||
void fe_0(fe h);
|
||||
void fe_1(fe h);
|
||||
|
||||
void fe_frombytes(fe h, const unsigned char *s);
|
||||
void fe_tobytes(unsigned char *s, const fe h);
|
||||
|
||||
void fe_copy(fe h, const fe f);
|
||||
int fe_isnegative(const fe f);
|
||||
int fe_isnonzero(const fe f);
|
||||
void fe_cmov(fe f, const fe g, unsigned int b);
|
||||
void fe_cswap(fe f, fe g, unsigned int b);
|
||||
|
||||
void fe_neg(fe h, const fe f);
|
||||
void fe_add(fe h, const fe f, const fe g);
|
||||
void fe_invert(fe out, const fe z);
|
||||
void fe_sq(fe h, const fe f);
|
||||
void fe_sq2(fe h, const fe f);
|
||||
void fe_mul(fe h, const fe f, const fe g);
|
||||
void fe_mul121666(fe h, fe f);
|
||||
void fe_pow22523(fe out, const fe z);
|
||||
void fe_sub(fe h, const fe f, const fe g);
|
||||
|
||||
#endif
|
||||
72
lib/ed25519/fixedint.h
Executable file
72
lib/ed25519/fixedint.h
Executable file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
Portable header to provide the 32 and 64 bits type.
|
||||
|
||||
Not a compatible replacement for <stdint.h>, do not blindly use it as such.
|
||||
*/
|
||||
|
||||
#if ((defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined(__WATCOMC__) && (defined(_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_) || defined(__UINT_FAST64_TYPE__)) )) && !defined(FIXEDINT_H_INCLUDED)
|
||||
#include <stdint.h>
|
||||
#define FIXEDINT_H_INCLUDED
|
||||
|
||||
#if defined(__WATCOMC__) && __WATCOMC__ >= 1250 && !defined(UINT64_C)
|
||||
#include <limits.h>
|
||||
#define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef FIXEDINT_H_INCLUDED
|
||||
#define FIXEDINT_H_INCLUDED
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
/* (u)int32_t */
|
||||
#ifndef uint32_t
|
||||
#if (ULONG_MAX == 0xffffffffUL)
|
||||
typedef unsigned long uint32_t;
|
||||
#elif (UINT_MAX == 0xffffffffUL)
|
||||
typedef unsigned int uint32_t;
|
||||
#elif (USHRT_MAX == 0xffffffffUL)
|
||||
typedef unsigned short uint32_t;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef int32_t
|
||||
#if (LONG_MAX == 0x7fffffffL)
|
||||
typedef signed long int32_t;
|
||||
#elif (INT_MAX == 0x7fffffffL)
|
||||
typedef signed int int32_t;
|
||||
#elif (SHRT_MAX == 0x7fffffffL)
|
||||
typedef signed short int32_t;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/* (u)int64_t */
|
||||
#if (defined(__STDC__) && defined(__STDC_VERSION__) && __STDC__ && __STDC_VERSION__ >= 199901L)
|
||||
typedef long long int64_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
|
||||
#define UINT64_C(v) v ##ULL
|
||||
#define INT64_C(v) v ##LL
|
||||
#elif defined(__GNUC__)
|
||||
__extension__ typedef long long int64_t;
|
||||
__extension__ typedef unsigned long long uint64_t;
|
||||
|
||||
#define UINT64_C(v) v ##ULL
|
||||
#define INT64_C(v) v ##LL
|
||||
#elif defined(__MWERKS__) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) || defined(__APPLE_CC__) || defined(_LONG_LONG) || defined(_CRAYC)
|
||||
typedef long long int64_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
|
||||
#define UINT64_C(v) v ##ULL
|
||||
#define INT64_C(v) v ##LL
|
||||
#elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined(__BORLANDC__) && __BORLANDC__ > 0x460) || defined(__alpha) || defined(__DECC)
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
|
||||
#define UINT64_C(v) v ##UI64
|
||||
#define INT64_C(v) v ##I64
|
||||
#endif
|
||||
#endif
|
||||
467
lib/ed25519/ge.c
Executable file
467
lib/ed25519/ge.c
Executable file
@@ -0,0 +1,467 @@
|
||||
#include "ge.h"
|
||||
#include "precomp_data.h"
|
||||
|
||||
|
||||
/*
|
||||
r = p + q
|
||||
*/
|
||||
|
||||
void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) {
|
||||
fe t0;
|
||||
fe_add(r->X, p->Y, p->X);
|
||||
fe_sub(r->Y, p->Y, p->X);
|
||||
fe_mul(r->Z, r->X, q->YplusX);
|
||||
fe_mul(r->Y, r->Y, q->YminusX);
|
||||
fe_mul(r->T, q->T2d, p->T);
|
||||
fe_mul(r->X, p->Z, q->Z);
|
||||
fe_add(t0, r->X, r->X);
|
||||
fe_sub(r->X, r->Z, r->Y);
|
||||
fe_add(r->Y, r->Z, r->Y);
|
||||
fe_add(r->Z, t0, r->T);
|
||||
fe_sub(r->T, t0, r->T);
|
||||
}
|
||||
|
||||
|
||||
static void slide(signed char *r, const unsigned char *a) {
|
||||
int i;
|
||||
int b;
|
||||
int k;
|
||||
|
||||
for (i = 0; i < 256; ++i) {
|
||||
r[i] = 1 & (a[i >> 3] >> (i & 7));
|
||||
}
|
||||
|
||||
for (i = 0; i < 256; ++i)
|
||||
if (r[i]) {
|
||||
for (b = 1; b <= 6 && i + b < 256; ++b) {
|
||||
if (r[i + b]) {
|
||||
if (r[i] + (r[i + b] << b) <= 15) {
|
||||
r[i] += r[i + b] << b;
|
||||
r[i + b] = 0;
|
||||
} else if (r[i] - (r[i + b] << b) >= -15) {
|
||||
r[i] -= r[i + b] << b;
|
||||
|
||||
for (k = i + b; k < 256; ++k) {
|
||||
if (!r[k]) {
|
||||
r[k] = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
r[k] = 0;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
r = a * A + b * B
|
||||
where a = a[0]+256*a[1]+...+256^31 a[31].
|
||||
and b = b[0]+256*b[1]+...+256^31 b[31].
|
||||
B is the Ed25519 base point (x,4/5) with x positive.
|
||||
*/
|
||||
|
||||
void ge_double_scalarmult_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b) {
|
||||
signed char aslide[256];
|
||||
signed char bslide[256];
|
||||
ge_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */
|
||||
ge_p1p1 t;
|
||||
ge_p3 u;
|
||||
ge_p3 A2;
|
||||
int i;
|
||||
slide(aslide, a);
|
||||
slide(bslide, b);
|
||||
ge_p3_to_cached(&Ai[0], A);
|
||||
ge_p3_dbl(&t, A);
|
||||
ge_p1p1_to_p3(&A2, &t);
|
||||
ge_add(&t, &A2, &Ai[0]);
|
||||
ge_p1p1_to_p3(&u, &t);
|
||||
ge_p3_to_cached(&Ai[1], &u);
|
||||
ge_add(&t, &A2, &Ai[1]);
|
||||
ge_p1p1_to_p3(&u, &t);
|
||||
ge_p3_to_cached(&Ai[2], &u);
|
||||
ge_add(&t, &A2, &Ai[2]);
|
||||
ge_p1p1_to_p3(&u, &t);
|
||||
ge_p3_to_cached(&Ai[3], &u);
|
||||
ge_add(&t, &A2, &Ai[3]);
|
||||
ge_p1p1_to_p3(&u, &t);
|
||||
ge_p3_to_cached(&Ai[4], &u);
|
||||
ge_add(&t, &A2, &Ai[4]);
|
||||
ge_p1p1_to_p3(&u, &t);
|
||||
ge_p3_to_cached(&Ai[5], &u);
|
||||
ge_add(&t, &A2, &Ai[5]);
|
||||
ge_p1p1_to_p3(&u, &t);
|
||||
ge_p3_to_cached(&Ai[6], &u);
|
||||
ge_add(&t, &A2, &Ai[6]);
|
||||
ge_p1p1_to_p3(&u, &t);
|
||||
ge_p3_to_cached(&Ai[7], &u);
|
||||
ge_p2_0(r);
|
||||
|
||||
for (i = 255; i >= 0; --i) {
|
||||
if (aslide[i] || bslide[i]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (; i >= 0; --i) {
|
||||
ge_p2_dbl(&t, r);
|
||||
|
||||
if (aslide[i] > 0) {
|
||||
ge_p1p1_to_p3(&u, &t);
|
||||
ge_add(&t, &u, &Ai[aslide[i] / 2]);
|
||||
} else if (aslide[i] < 0) {
|
||||
ge_p1p1_to_p3(&u, &t);
|
||||
ge_sub(&t, &u, &Ai[(-aslide[i]) / 2]);
|
||||
}
|
||||
|
||||
if (bslide[i] > 0) {
|
||||
ge_p1p1_to_p3(&u, &t);
|
||||
ge_madd(&t, &u, &Bi[bslide[i] / 2]);
|
||||
} else if (bslide[i] < 0) {
|
||||
ge_p1p1_to_p3(&u, &t);
|
||||
ge_msub(&t, &u, &Bi[(-bslide[i]) / 2]);
|
||||
}
|
||||
|
||||
ge_p1p1_to_p2(r, &t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const fe d = {
|
||||
-10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116
|
||||
};
|
||||
|
||||
static const fe sqrtm1 = {
|
||||
-32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482
|
||||
};
|
||||
|
||||
int ge_frombytes_negate_vartime(ge_p3 *h, const unsigned char *s) {
|
||||
fe u;
|
||||
fe v;
|
||||
fe v3;
|
||||
fe vxx;
|
||||
fe check;
|
||||
fe_frombytes(h->Y, s);
|
||||
fe_1(h->Z);
|
||||
fe_sq(u, h->Y);
|
||||
fe_mul(v, u, d);
|
||||
fe_sub(u, u, h->Z); /* u = y^2-1 */
|
||||
fe_add(v, v, h->Z); /* v = dy^2+1 */
|
||||
fe_sq(v3, v);
|
||||
fe_mul(v3, v3, v); /* v3 = v^3 */
|
||||
fe_sq(h->X, v3);
|
||||
fe_mul(h->X, h->X, v);
|
||||
fe_mul(h->X, h->X, u); /* x = uv^7 */
|
||||
fe_pow22523(h->X, h->X); /* x = (uv^7)^((q-5)/8) */
|
||||
fe_mul(h->X, h->X, v3);
|
||||
fe_mul(h->X, h->X, u); /* x = uv^3(uv^7)^((q-5)/8) */
|
||||
fe_sq(vxx, h->X);
|
||||
fe_mul(vxx, vxx, v);
|
||||
fe_sub(check, vxx, u); /* vx^2-u */
|
||||
|
||||
if (fe_isnonzero(check)) {
|
||||
fe_add(check, vxx, u); /* vx^2+u */
|
||||
|
||||
if (fe_isnonzero(check)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
fe_mul(h->X, h->X, sqrtm1);
|
||||
}
|
||||
|
||||
if (fe_isnegative(h->X) == (s[31] >> 7)) {
|
||||
fe_neg(h->X, h->X);
|
||||
}
|
||||
|
||||
fe_mul(h->T, h->X, h->Y);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
r = p + q
|
||||
*/
|
||||
|
||||
void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) {
|
||||
fe t0;
|
||||
fe_add(r->X, p->Y, p->X);
|
||||
fe_sub(r->Y, p->Y, p->X);
|
||||
fe_mul(r->Z, r->X, q->yplusx);
|
||||
fe_mul(r->Y, r->Y, q->yminusx);
|
||||
fe_mul(r->T, q->xy2d, p->T);
|
||||
fe_add(t0, p->Z, p->Z);
|
||||
fe_sub(r->X, r->Z, r->Y);
|
||||
fe_add(r->Y, r->Z, r->Y);
|
||||
fe_add(r->Z, t0, r->T);
|
||||
fe_sub(r->T, t0, r->T);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
r = p - q
|
||||
*/
|
||||
|
||||
void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) {
|
||||
fe t0;
|
||||
|
||||
fe_add(r->X, p->Y, p->X);
|
||||
fe_sub(r->Y, p->Y, p->X);
|
||||
fe_mul(r->Z, r->X, q->yminusx);
|
||||
fe_mul(r->Y, r->Y, q->yplusx);
|
||||
fe_mul(r->T, q->xy2d, p->T);
|
||||
fe_add(t0, p->Z, p->Z);
|
||||
fe_sub(r->X, r->Z, r->Y);
|
||||
fe_add(r->Y, r->Z, r->Y);
|
||||
fe_sub(r->Z, t0, r->T);
|
||||
fe_add(r->T, t0, r->T);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
r = p
|
||||
*/
|
||||
|
||||
void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p) {
|
||||
fe_mul(r->X, p->X, p->T);
|
||||
fe_mul(r->Y, p->Y, p->Z);
|
||||
fe_mul(r->Z, p->Z, p->T);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
r = p
|
||||
*/
|
||||
|
||||
void ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p) {
|
||||
fe_mul(r->X, p->X, p->T);
|
||||
fe_mul(r->Y, p->Y, p->Z);
|
||||
fe_mul(r->Z, p->Z, p->T);
|
||||
fe_mul(r->T, p->X, p->Y);
|
||||
}
|
||||
|
||||
|
||||
void ge_p2_0(ge_p2 *h) {
|
||||
fe_0(h->X);
|
||||
fe_1(h->Y);
|
||||
fe_1(h->Z);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
r = 2 * p
|
||||
*/
|
||||
|
||||
void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p) {
|
||||
fe t0;
|
||||
|
||||
fe_sq(r->X, p->X);
|
||||
fe_sq(r->Z, p->Y);
|
||||
fe_sq2(r->T, p->Z);
|
||||
fe_add(r->Y, p->X, p->Y);
|
||||
fe_sq(t0, r->Y);
|
||||
fe_add(r->Y, r->Z, r->X);
|
||||
fe_sub(r->Z, r->Z, r->X);
|
||||
fe_sub(r->X, t0, r->Y);
|
||||
fe_sub(r->T, r->T, r->Z);
|
||||
}
|
||||
|
||||
|
||||
void ge_p3_0(ge_p3 *h) {
|
||||
fe_0(h->X);
|
||||
fe_1(h->Y);
|
||||
fe_1(h->Z);
|
||||
fe_0(h->T);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
r = 2 * p
|
||||
*/
|
||||
|
||||
void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p) {
|
||||
ge_p2 q;
|
||||
ge_p3_to_p2(&q, p);
|
||||
ge_p2_dbl(r, &q);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
r = p
|
||||
*/
|
||||
|
||||
static const fe d2 = {
|
||||
-21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199
|
||||
};
|
||||
|
||||
void ge_p3_to_cached(ge_cached *r, const ge_p3 *p) {
|
||||
fe_add(r->YplusX, p->Y, p->X);
|
||||
fe_sub(r->YminusX, p->Y, p->X);
|
||||
fe_copy(r->Z, p->Z);
|
||||
fe_mul(r->T2d, p->T, d2);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
r = p
|
||||
*/
|
||||
|
||||
void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p) {
|
||||
fe_copy(r->X, p->X);
|
||||
fe_copy(r->Y, p->Y);
|
||||
fe_copy(r->Z, p->Z);
|
||||
}
|
||||
|
||||
|
||||
void ge_p3_tobytes(unsigned char *s, const ge_p3 *h) {
|
||||
fe recip;
|
||||
fe x;
|
||||
fe y;
|
||||
fe_invert(recip, h->Z);
|
||||
fe_mul(x, h->X, recip);
|
||||
fe_mul(y, h->Y, recip);
|
||||
fe_tobytes(s, y);
|
||||
s[31] ^= fe_isnegative(x) << 7;
|
||||
}
|
||||
|
||||
|
||||
static unsigned char equal(signed char b, signed char c) {
|
||||
unsigned char ub = b;
|
||||
unsigned char uc = c;
|
||||
unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */
|
||||
uint64_t y = x; /* 0: yes; 1..255: no */
|
||||
y -= 1; /* large: yes; 0..254: no */
|
||||
y >>= 63; /* 1: yes; 0: no */
|
||||
return (unsigned char) y;
|
||||
}
|
||||
|
||||
static unsigned char negative(signed char b) {
|
||||
uint64_t x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */
|
||||
x >>= 63; /* 1: yes; 0: no */
|
||||
return (unsigned char) x;
|
||||
}
|
||||
|
||||
static void cmov(ge_precomp *t, const ge_precomp *u, unsigned char b) {
|
||||
fe_cmov(t->yplusx, u->yplusx, b);
|
||||
fe_cmov(t->yminusx, u->yminusx, b);
|
||||
fe_cmov(t->xy2d, u->xy2d, b);
|
||||
}
|
||||
|
||||
|
||||
static void select(ge_precomp *t, int pos, signed char b) {
|
||||
ge_precomp minust;
|
||||
unsigned char bnegative = negative(b);
|
||||
unsigned char babs = b - (((-bnegative) & b) << 1);
|
||||
fe_1(t->yplusx);
|
||||
fe_1(t->yminusx);
|
||||
fe_0(t->xy2d);
|
||||
cmov(t, &base[pos][0], equal(babs, 1));
|
||||
cmov(t, &base[pos][1], equal(babs, 2));
|
||||
cmov(t, &base[pos][2], equal(babs, 3));
|
||||
cmov(t, &base[pos][3], equal(babs, 4));
|
||||
cmov(t, &base[pos][4], equal(babs, 5));
|
||||
cmov(t, &base[pos][5], equal(babs, 6));
|
||||
cmov(t, &base[pos][6], equal(babs, 7));
|
||||
cmov(t, &base[pos][7], equal(babs, 8));
|
||||
fe_copy(minust.yplusx, t->yminusx);
|
||||
fe_copy(minust.yminusx, t->yplusx);
|
||||
fe_neg(minust.xy2d, t->xy2d);
|
||||
cmov(t, &minust, bnegative);
|
||||
}
|
||||
|
||||
/*
|
||||
h = a * B
|
||||
where a = a[0]+256*a[1]+...+256^31 a[31]
|
||||
B is the Ed25519 base point (x,4/5) with x positive.
|
||||
|
||||
Preconditions:
|
||||
a[31] <= 127
|
||||
*/
|
||||
|
||||
void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) {
|
||||
signed char e[64];
|
||||
signed char carry;
|
||||
ge_p1p1 r;
|
||||
ge_p2 s;
|
||||
ge_precomp t;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 32; ++i) {
|
||||
e[2 * i + 0] = (a[i] >> 0) & 15;
|
||||
e[2 * i + 1] = (a[i] >> 4) & 15;
|
||||
}
|
||||
|
||||
/* each e[i] is between 0 and 15 */
|
||||
/* e[63] is between 0 and 7 */
|
||||
carry = 0;
|
||||
|
||||
for (i = 0; i < 63; ++i) {
|
||||
e[i] += carry;
|
||||
carry = e[i] + 8;
|
||||
carry >>= 4;
|
||||
e[i] -= carry << 4;
|
||||
}
|
||||
|
||||
e[63] += carry;
|
||||
/* each e[i] is between -8 and 8 */
|
||||
ge_p3_0(h);
|
||||
|
||||
for (i = 1; i < 64; i += 2) {
|
||||
select(&t, i / 2, e[i]);
|
||||
ge_madd(&r, h, &t);
|
||||
ge_p1p1_to_p3(h, &r);
|
||||
}
|
||||
|
||||
ge_p3_dbl(&r, h);
|
||||
ge_p1p1_to_p2(&s, &r);
|
||||
ge_p2_dbl(&r, &s);
|
||||
ge_p1p1_to_p2(&s, &r);
|
||||
ge_p2_dbl(&r, &s);
|
||||
ge_p1p1_to_p2(&s, &r);
|
||||
ge_p2_dbl(&r, &s);
|
||||
ge_p1p1_to_p3(h, &r);
|
||||
|
||||
for (i = 0; i < 64; i += 2) {
|
||||
select(&t, i / 2, e[i]);
|
||||
ge_madd(&r, h, &t);
|
||||
ge_p1p1_to_p3(h, &r);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
r = p - q
|
||||
*/
|
||||
|
||||
void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) {
|
||||
fe t0;
|
||||
|
||||
fe_add(r->X, p->Y, p->X);
|
||||
fe_sub(r->Y, p->Y, p->X);
|
||||
fe_mul(r->Z, r->X, q->YminusX);
|
||||
fe_mul(r->Y, r->Y, q->YplusX);
|
||||
fe_mul(r->T, q->T2d, p->T);
|
||||
fe_mul(r->X, p->Z, q->Z);
|
||||
fe_add(t0, r->X, r->X);
|
||||
fe_sub(r->X, r->Z, r->Y);
|
||||
fe_add(r->Y, r->Z, r->Y);
|
||||
fe_sub(r->Z, t0, r->T);
|
||||
fe_add(r->T, t0, r->T);
|
||||
}
|
||||
|
||||
|
||||
void ge_tobytes(unsigned char *s, const ge_p2 *h) {
|
||||
fe recip;
|
||||
fe x;
|
||||
fe y;
|
||||
fe_invert(recip, h->Z);
|
||||
fe_mul(x, h->X, recip);
|
||||
fe_mul(y, h->Y, recip);
|
||||
fe_tobytes(s, y);
|
||||
s[31] ^= fe_isnegative(x) << 7;
|
||||
}
|
||||
74
lib/ed25519/ge.h
Executable file
74
lib/ed25519/ge.h
Executable file
@@ -0,0 +1,74 @@
|
||||
#ifndef GE_H
|
||||
#define GE_H
|
||||
|
||||
#include "fe.h"
|
||||
|
||||
|
||||
/*
|
||||
ge means group element.
|
||||
|
||||
Here the group is the set of pairs (x,y) of field elements (see fe.h)
|
||||
satisfying -x^2 + y^2 = 1 + d x^2y^2
|
||||
where d = -121665/121666.
|
||||
|
||||
Representations:
|
||||
ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z
|
||||
ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT
|
||||
ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T
|
||||
ge_precomp (Duif): (y+x,y-x,2dxy)
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
fe X;
|
||||
fe Y;
|
||||
fe Z;
|
||||
} ge_p2;
|
||||
|
||||
typedef struct {
|
||||
fe X;
|
||||
fe Y;
|
||||
fe Z;
|
||||
fe T;
|
||||
} ge_p3;
|
||||
|
||||
typedef struct {
|
||||
fe X;
|
||||
fe Y;
|
||||
fe Z;
|
||||
fe T;
|
||||
} ge_p1p1;
|
||||
|
||||
typedef struct {
|
||||
fe yplusx;
|
||||
fe yminusx;
|
||||
fe xy2d;
|
||||
} ge_precomp;
|
||||
|
||||
typedef struct {
|
||||
fe YplusX;
|
||||
fe YminusX;
|
||||
fe Z;
|
||||
fe T2d;
|
||||
} ge_cached;
|
||||
|
||||
void ge_p3_tobytes(unsigned char *s, const ge_p3 *h);
|
||||
void ge_tobytes(unsigned char *s, const ge_p2 *h);
|
||||
int ge_frombytes_negate_vartime(ge_p3 *h, const unsigned char *s);
|
||||
|
||||
void ge_add(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q);
|
||||
void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q);
|
||||
void ge_double_scalarmult_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b);
|
||||
void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q);
|
||||
void ge_msub(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q);
|
||||
void ge_scalarmult_base(ge_p3 *h, const unsigned char *a);
|
||||
|
||||
void ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p);
|
||||
void ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p);
|
||||
void ge_p2_0(ge_p2 *h);
|
||||
void ge_p2_dbl(ge_p1p1 *r, const ge_p2 *p);
|
||||
void ge_p3_0(ge_p3 *h);
|
||||
void ge_p3_dbl(ge_p1p1 *r, const ge_p3 *p);
|
||||
void ge_p3_to_cached(ge_cached *r, const ge_p3 *p);
|
||||
void ge_p3_to_p2(ge_p2 *r, const ge_p3 *p);
|
||||
|
||||
#endif
|
||||
79
lib/ed25519/key_exchange.c
Executable file
79
lib/ed25519/key_exchange.c
Executable file
@@ -0,0 +1,79 @@
|
||||
#include "ed25519.h"
|
||||
#include "fe.h"
|
||||
|
||||
void ed25519_key_exchange(unsigned char *shared_secret, const unsigned char *public_key, const unsigned char *private_key) {
|
||||
unsigned char e[32];
|
||||
unsigned int i;
|
||||
|
||||
fe x1;
|
||||
fe x2;
|
||||
fe z2;
|
||||
fe x3;
|
||||
fe z3;
|
||||
fe tmp0;
|
||||
fe tmp1;
|
||||
|
||||
int pos;
|
||||
unsigned int swap;
|
||||
unsigned int b;
|
||||
|
||||
/* copy the private key and make sure it's valid */
|
||||
for (i = 0; i < 32; ++i) {
|
||||
e[i] = private_key[i];
|
||||
}
|
||||
|
||||
e[0] &= 248;
|
||||
e[31] &= 63;
|
||||
e[31] |= 64;
|
||||
|
||||
/* unpack the public key and convert edwards to montgomery */
|
||||
/* due to CodesInChaos: montgomeryX = (edwardsY + 1)*inverse(1 - edwardsY) mod p */
|
||||
fe_frombytes(x1, public_key);
|
||||
fe_1(tmp1);
|
||||
fe_add(tmp0, x1, tmp1);
|
||||
fe_sub(tmp1, tmp1, x1);
|
||||
fe_invert(tmp1, tmp1);
|
||||
fe_mul(x1, tmp0, tmp1);
|
||||
|
||||
fe_1(x2);
|
||||
fe_0(z2);
|
||||
fe_copy(x3, x1);
|
||||
fe_1(z3);
|
||||
|
||||
swap = 0;
|
||||
for (pos = 254; pos >= 0; --pos) {
|
||||
b = e[pos / 8] >> (pos & 7);
|
||||
b &= 1;
|
||||
swap ^= b;
|
||||
fe_cswap(x2, x3, swap);
|
||||
fe_cswap(z2, z3, swap);
|
||||
swap = b;
|
||||
|
||||
/* from montgomery.h */
|
||||
fe_sub(tmp0, x3, z3);
|
||||
fe_sub(tmp1, x2, z2);
|
||||
fe_add(x2, x2, z2);
|
||||
fe_add(z2, x3, z3);
|
||||
fe_mul(z3, tmp0, x2);
|
||||
fe_mul(z2, z2, tmp1);
|
||||
fe_sq(tmp0, tmp1);
|
||||
fe_sq(tmp1, x2);
|
||||
fe_add(x3, z3, z2);
|
||||
fe_sub(z2, z3, z2);
|
||||
fe_mul(x2, tmp1, tmp0);
|
||||
fe_sub(tmp1, tmp1, tmp0);
|
||||
fe_sq(z2, z2);
|
||||
fe_mul121666(z3, tmp1);
|
||||
fe_sq(x3, x3);
|
||||
fe_add(tmp0, tmp0, z3);
|
||||
fe_mul(z3, x1, z2);
|
||||
fe_mul(z2, tmp1, tmp0);
|
||||
}
|
||||
|
||||
fe_cswap(x2, x3, swap);
|
||||
fe_cswap(z2, z3, swap);
|
||||
|
||||
fe_invert(z2, z2);
|
||||
fe_mul(x2, x2, z2);
|
||||
fe_tobytes(shared_secret, x2);
|
||||
}
|
||||
16
lib/ed25519/keypair.c
Executable file
16
lib/ed25519/keypair.c
Executable file
@@ -0,0 +1,16 @@
|
||||
#include "ed25519.h"
|
||||
#include "sha512.h"
|
||||
#include "ge.h"
|
||||
|
||||
|
||||
void ed25519_create_keypair(unsigned char *public_key, unsigned char *private_key, const unsigned char *seed) {
|
||||
ge_p3 A;
|
||||
|
||||
sha512(seed, 32, private_key);
|
||||
private_key[0] &= 248;
|
||||
private_key[31] &= 63;
|
||||
private_key[31] |= 64;
|
||||
|
||||
ge_scalarmult_base(&A, private_key);
|
||||
ge_p3_tobytes(public_key, &A);
|
||||
}
|
||||
1391
lib/ed25519/precomp_data.h
Executable file
1391
lib/ed25519/precomp_data.h
Executable file
File diff suppressed because it is too large
Load Diff
809
lib/ed25519/sc.c
Executable file
809
lib/ed25519/sc.c
Executable file
@@ -0,0 +1,809 @@
|
||||
#include "fixedint.h"
|
||||
#include "sc.h"
|
||||
|
||||
static uint64_t load_3(const unsigned char *in) {
|
||||
uint64_t result;
|
||||
|
||||
result = (uint64_t) in[0];
|
||||
result |= ((uint64_t) in[1]) << 8;
|
||||
result |= ((uint64_t) in[2]) << 16;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint64_t load_4(const unsigned char *in) {
|
||||
uint64_t result;
|
||||
|
||||
result = (uint64_t) in[0];
|
||||
result |= ((uint64_t) in[1]) << 8;
|
||||
result |= ((uint64_t) in[2]) << 16;
|
||||
result |= ((uint64_t) in[3]) << 24;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
Input:
|
||||
s[0]+256*s[1]+...+256^63*s[63] = s
|
||||
|
||||
Output:
|
||||
s[0]+256*s[1]+...+256^31*s[31] = s mod l
|
||||
where l = 2^252 + 27742317777372353535851937790883648493.
|
||||
Overwrites s in place.
|
||||
*/
|
||||
|
||||
void sc_reduce(unsigned char *s) {
|
||||
int64_t s0 = 2097151 & load_3(s);
|
||||
int64_t s1 = 2097151 & (load_4(s + 2) >> 5);
|
||||
int64_t s2 = 2097151 & (load_3(s + 5) >> 2);
|
||||
int64_t s3 = 2097151 & (load_4(s + 7) >> 7);
|
||||
int64_t s4 = 2097151 & (load_4(s + 10) >> 4);
|
||||
int64_t s5 = 2097151 & (load_3(s + 13) >> 1);
|
||||
int64_t s6 = 2097151 & (load_4(s + 15) >> 6);
|
||||
int64_t s7 = 2097151 & (load_3(s + 18) >> 3);
|
||||
int64_t s8 = 2097151 & load_3(s + 21);
|
||||
int64_t s9 = 2097151 & (load_4(s + 23) >> 5);
|
||||
int64_t s10 = 2097151 & (load_3(s + 26) >> 2);
|
||||
int64_t s11 = 2097151 & (load_4(s + 28) >> 7);
|
||||
int64_t s12 = 2097151 & (load_4(s + 31) >> 4);
|
||||
int64_t s13 = 2097151 & (load_3(s + 34) >> 1);
|
||||
int64_t s14 = 2097151 & (load_4(s + 36) >> 6);
|
||||
int64_t s15 = 2097151 & (load_3(s + 39) >> 3);
|
||||
int64_t s16 = 2097151 & load_3(s + 42);
|
||||
int64_t s17 = 2097151 & (load_4(s + 44) >> 5);
|
||||
int64_t s18 = 2097151 & (load_3(s + 47) >> 2);
|
||||
int64_t s19 = 2097151 & (load_4(s + 49) >> 7);
|
||||
int64_t s20 = 2097151 & (load_4(s + 52) >> 4);
|
||||
int64_t s21 = 2097151 & (load_3(s + 55) >> 1);
|
||||
int64_t s22 = 2097151 & (load_4(s + 57) >> 6);
|
||||
int64_t s23 = (load_4(s + 60) >> 3);
|
||||
int64_t carry0;
|
||||
int64_t carry1;
|
||||
int64_t carry2;
|
||||
int64_t carry3;
|
||||
int64_t carry4;
|
||||
int64_t carry5;
|
||||
int64_t carry6;
|
||||
int64_t carry7;
|
||||
int64_t carry8;
|
||||
int64_t carry9;
|
||||
int64_t carry10;
|
||||
int64_t carry11;
|
||||
int64_t carry12;
|
||||
int64_t carry13;
|
||||
int64_t carry14;
|
||||
int64_t carry15;
|
||||
int64_t carry16;
|
||||
|
||||
s11 += s23 * 666643;
|
||||
s12 += s23 * 470296;
|
||||
s13 += s23 * 654183;
|
||||
s14 -= s23 * 997805;
|
||||
s15 += s23 * 136657;
|
||||
s16 -= s23 * 683901;
|
||||
s23 = 0;
|
||||
s10 += s22 * 666643;
|
||||
s11 += s22 * 470296;
|
||||
s12 += s22 * 654183;
|
||||
s13 -= s22 * 997805;
|
||||
s14 += s22 * 136657;
|
||||
s15 -= s22 * 683901;
|
||||
s22 = 0;
|
||||
s9 += s21 * 666643;
|
||||
s10 += s21 * 470296;
|
||||
s11 += s21 * 654183;
|
||||
s12 -= s21 * 997805;
|
||||
s13 += s21 * 136657;
|
||||
s14 -= s21 * 683901;
|
||||
s21 = 0;
|
||||
s8 += s20 * 666643;
|
||||
s9 += s20 * 470296;
|
||||
s10 += s20 * 654183;
|
||||
s11 -= s20 * 997805;
|
||||
s12 += s20 * 136657;
|
||||
s13 -= s20 * 683901;
|
||||
s20 = 0;
|
||||
s7 += s19 * 666643;
|
||||
s8 += s19 * 470296;
|
||||
s9 += s19 * 654183;
|
||||
s10 -= s19 * 997805;
|
||||
s11 += s19 * 136657;
|
||||
s12 -= s19 * 683901;
|
||||
s19 = 0;
|
||||
s6 += s18 * 666643;
|
||||
s7 += s18 * 470296;
|
||||
s8 += s18 * 654183;
|
||||
s9 -= s18 * 997805;
|
||||
s10 += s18 * 136657;
|
||||
s11 -= s18 * 683901;
|
||||
s18 = 0;
|
||||
carry6 = (s6 + (1 << 20)) >> 21;
|
||||
s7 += carry6;
|
||||
s6 -= carry6 << 21;
|
||||
carry8 = (s8 + (1 << 20)) >> 21;
|
||||
s9 += carry8;
|
||||
s8 -= carry8 << 21;
|
||||
carry10 = (s10 + (1 << 20)) >> 21;
|
||||
s11 += carry10;
|
||||
s10 -= carry10 << 21;
|
||||
carry12 = (s12 + (1 << 20)) >> 21;
|
||||
s13 += carry12;
|
||||
s12 -= carry12 << 21;
|
||||
carry14 = (s14 + (1 << 20)) >> 21;
|
||||
s15 += carry14;
|
||||
s14 -= carry14 << 21;
|
||||
carry16 = (s16 + (1 << 20)) >> 21;
|
||||
s17 += carry16;
|
||||
s16 -= carry16 << 21;
|
||||
carry7 = (s7 + (1 << 20)) >> 21;
|
||||
s8 += carry7;
|
||||
s7 -= carry7 << 21;
|
||||
carry9 = (s9 + (1 << 20)) >> 21;
|
||||
s10 += carry9;
|
||||
s9 -= carry9 << 21;
|
||||
carry11 = (s11 + (1 << 20)) >> 21;
|
||||
s12 += carry11;
|
||||
s11 -= carry11 << 21;
|
||||
carry13 = (s13 + (1 << 20)) >> 21;
|
||||
s14 += carry13;
|
||||
s13 -= carry13 << 21;
|
||||
carry15 = (s15 + (1 << 20)) >> 21;
|
||||
s16 += carry15;
|
||||
s15 -= carry15 << 21;
|
||||
s5 += s17 * 666643;
|
||||
s6 += s17 * 470296;
|
||||
s7 += s17 * 654183;
|
||||
s8 -= s17 * 997805;
|
||||
s9 += s17 * 136657;
|
||||
s10 -= s17 * 683901;
|
||||
s17 = 0;
|
||||
s4 += s16 * 666643;
|
||||
s5 += s16 * 470296;
|
||||
s6 += s16 * 654183;
|
||||
s7 -= s16 * 997805;
|
||||
s8 += s16 * 136657;
|
||||
s9 -= s16 * 683901;
|
||||
s16 = 0;
|
||||
s3 += s15 * 666643;
|
||||
s4 += s15 * 470296;
|
||||
s5 += s15 * 654183;
|
||||
s6 -= s15 * 997805;
|
||||
s7 += s15 * 136657;
|
||||
s8 -= s15 * 683901;
|
||||
s15 = 0;
|
||||
s2 += s14 * 666643;
|
||||
s3 += s14 * 470296;
|
||||
s4 += s14 * 654183;
|
||||
s5 -= s14 * 997805;
|
||||
s6 += s14 * 136657;
|
||||
s7 -= s14 * 683901;
|
||||
s14 = 0;
|
||||
s1 += s13 * 666643;
|
||||
s2 += s13 * 470296;
|
||||
s3 += s13 * 654183;
|
||||
s4 -= s13 * 997805;
|
||||
s5 += s13 * 136657;
|
||||
s6 -= s13 * 683901;
|
||||
s13 = 0;
|
||||
s0 += s12 * 666643;
|
||||
s1 += s12 * 470296;
|
||||
s2 += s12 * 654183;
|
||||
s3 -= s12 * 997805;
|
||||
s4 += s12 * 136657;
|
||||
s5 -= s12 * 683901;
|
||||
s12 = 0;
|
||||
carry0 = (s0 + (1 << 20)) >> 21;
|
||||
s1 += carry0;
|
||||
s0 -= carry0 << 21;
|
||||
carry2 = (s2 + (1 << 20)) >> 21;
|
||||
s3 += carry2;
|
||||
s2 -= carry2 << 21;
|
||||
carry4 = (s4 + (1 << 20)) >> 21;
|
||||
s5 += carry4;
|
||||
s4 -= carry4 << 21;
|
||||
carry6 = (s6 + (1 << 20)) >> 21;
|
||||
s7 += carry6;
|
||||
s6 -= carry6 << 21;
|
||||
carry8 = (s8 + (1 << 20)) >> 21;
|
||||
s9 += carry8;
|
||||
s8 -= carry8 << 21;
|
||||
carry10 = (s10 + (1 << 20)) >> 21;
|
||||
s11 += carry10;
|
||||
s10 -= carry10 << 21;
|
||||
carry1 = (s1 + (1 << 20)) >> 21;
|
||||
s2 += carry1;
|
||||
s1 -= carry1 << 21;
|
||||
carry3 = (s3 + (1 << 20)) >> 21;
|
||||
s4 += carry3;
|
||||
s3 -= carry3 << 21;
|
||||
carry5 = (s5 + (1 << 20)) >> 21;
|
||||
s6 += carry5;
|
||||
s5 -= carry5 << 21;
|
||||
carry7 = (s7 + (1 << 20)) >> 21;
|
||||
s8 += carry7;
|
||||
s7 -= carry7 << 21;
|
||||
carry9 = (s9 + (1 << 20)) >> 21;
|
||||
s10 += carry9;
|
||||
s9 -= carry9 << 21;
|
||||
carry11 = (s11 + (1 << 20)) >> 21;
|
||||
s12 += carry11;
|
||||
s11 -= carry11 << 21;
|
||||
s0 += s12 * 666643;
|
||||
s1 += s12 * 470296;
|
||||
s2 += s12 * 654183;
|
||||
s3 -= s12 * 997805;
|
||||
s4 += s12 * 136657;
|
||||
s5 -= s12 * 683901;
|
||||
s12 = 0;
|
||||
carry0 = s0 >> 21;
|
||||
s1 += carry0;
|
||||
s0 -= carry0 << 21;
|
||||
carry1 = s1 >> 21;
|
||||
s2 += carry1;
|
||||
s1 -= carry1 << 21;
|
||||
carry2 = s2 >> 21;
|
||||
s3 += carry2;
|
||||
s2 -= carry2 << 21;
|
||||
carry3 = s3 >> 21;
|
||||
s4 += carry3;
|
||||
s3 -= carry3 << 21;
|
||||
carry4 = s4 >> 21;
|
||||
s5 += carry4;
|
||||
s4 -= carry4 << 21;
|
||||
carry5 = s5 >> 21;
|
||||
s6 += carry5;
|
||||
s5 -= carry5 << 21;
|
||||
carry6 = s6 >> 21;
|
||||
s7 += carry6;
|
||||
s6 -= carry6 << 21;
|
||||
carry7 = s7 >> 21;
|
||||
s8 += carry7;
|
||||
s7 -= carry7 << 21;
|
||||
carry8 = s8 >> 21;
|
||||
s9 += carry8;
|
||||
s8 -= carry8 << 21;
|
||||
carry9 = s9 >> 21;
|
||||
s10 += carry9;
|
||||
s9 -= carry9 << 21;
|
||||
carry10 = s10 >> 21;
|
||||
s11 += carry10;
|
||||
s10 -= carry10 << 21;
|
||||
carry11 = s11 >> 21;
|
||||
s12 += carry11;
|
||||
s11 -= carry11 << 21;
|
||||
s0 += s12 * 666643;
|
||||
s1 += s12 * 470296;
|
||||
s2 += s12 * 654183;
|
||||
s3 -= s12 * 997805;
|
||||
s4 += s12 * 136657;
|
||||
s5 -= s12 * 683901;
|
||||
s12 = 0;
|
||||
carry0 = s0 >> 21;
|
||||
s1 += carry0;
|
||||
s0 -= carry0 << 21;
|
||||
carry1 = s1 >> 21;
|
||||
s2 += carry1;
|
||||
s1 -= carry1 << 21;
|
||||
carry2 = s2 >> 21;
|
||||
s3 += carry2;
|
||||
s2 -= carry2 << 21;
|
||||
carry3 = s3 >> 21;
|
||||
s4 += carry3;
|
||||
s3 -= carry3 << 21;
|
||||
carry4 = s4 >> 21;
|
||||
s5 += carry4;
|
||||
s4 -= carry4 << 21;
|
||||
carry5 = s5 >> 21;
|
||||
s6 += carry5;
|
||||
s5 -= carry5 << 21;
|
||||
carry6 = s6 >> 21;
|
||||
s7 += carry6;
|
||||
s6 -= carry6 << 21;
|
||||
carry7 = s7 >> 21;
|
||||
s8 += carry7;
|
||||
s7 -= carry7 << 21;
|
||||
carry8 = s8 >> 21;
|
||||
s9 += carry8;
|
||||
s8 -= carry8 << 21;
|
||||
carry9 = s9 >> 21;
|
||||
s10 += carry9;
|
||||
s9 -= carry9 << 21;
|
||||
carry10 = s10 >> 21;
|
||||
s11 += carry10;
|
||||
s10 -= carry10 << 21;
|
||||
|
||||
s[0] = (unsigned char) (s0 >> 0);
|
||||
s[1] = (unsigned char) (s0 >> 8);
|
||||
s[2] = (unsigned char) ((s0 >> 16) | (s1 << 5));
|
||||
s[3] = (unsigned char) (s1 >> 3);
|
||||
s[4] = (unsigned char) (s1 >> 11);
|
||||
s[5] = (unsigned char) ((s1 >> 19) | (s2 << 2));
|
||||
s[6] = (unsigned char) (s2 >> 6);
|
||||
s[7] = (unsigned char) ((s2 >> 14) | (s3 << 7));
|
||||
s[8] = (unsigned char) (s3 >> 1);
|
||||
s[9] = (unsigned char) (s3 >> 9);
|
||||
s[10] = (unsigned char) ((s3 >> 17) | (s4 << 4));
|
||||
s[11] = (unsigned char) (s4 >> 4);
|
||||
s[12] = (unsigned char) (s4 >> 12);
|
||||
s[13] = (unsigned char) ((s4 >> 20) | (s5 << 1));
|
||||
s[14] = (unsigned char) (s5 >> 7);
|
||||
s[15] = (unsigned char) ((s5 >> 15) | (s6 << 6));
|
||||
s[16] = (unsigned char) (s6 >> 2);
|
||||
s[17] = (unsigned char) (s6 >> 10);
|
||||
s[18] = (unsigned char) ((s6 >> 18) | (s7 << 3));
|
||||
s[19] = (unsigned char) (s7 >> 5);
|
||||
s[20] = (unsigned char) (s7 >> 13);
|
||||
s[21] = (unsigned char) (s8 >> 0);
|
||||
s[22] = (unsigned char) (s8 >> 8);
|
||||
s[23] = (unsigned char) ((s8 >> 16) | (s9 << 5));
|
||||
s[24] = (unsigned char) (s9 >> 3);
|
||||
s[25] = (unsigned char) (s9 >> 11);
|
||||
s[26] = (unsigned char) ((s9 >> 19) | (s10 << 2));
|
||||
s[27] = (unsigned char) (s10 >> 6);
|
||||
s[28] = (unsigned char) ((s10 >> 14) | (s11 << 7));
|
||||
s[29] = (unsigned char) (s11 >> 1);
|
||||
s[30] = (unsigned char) (s11 >> 9);
|
||||
s[31] = (unsigned char) (s11 >> 17);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Input:
|
||||
a[0]+256*a[1]+...+256^31*a[31] = a
|
||||
b[0]+256*b[1]+...+256^31*b[31] = b
|
||||
c[0]+256*c[1]+...+256^31*c[31] = c
|
||||
|
||||
Output:
|
||||
s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l
|
||||
where l = 2^252 + 27742317777372353535851937790883648493.
|
||||
*/
|
||||
|
||||
void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c) {
|
||||
int64_t a0 = 2097151 & load_3(a);
|
||||
int64_t a1 = 2097151 & (load_4(a + 2) >> 5);
|
||||
int64_t a2 = 2097151 & (load_3(a + 5) >> 2);
|
||||
int64_t a3 = 2097151 & (load_4(a + 7) >> 7);
|
||||
int64_t a4 = 2097151 & (load_4(a + 10) >> 4);
|
||||
int64_t a5 = 2097151 & (load_3(a + 13) >> 1);
|
||||
int64_t a6 = 2097151 & (load_4(a + 15) >> 6);
|
||||
int64_t a7 = 2097151 & (load_3(a + 18) >> 3);
|
||||
int64_t a8 = 2097151 & load_3(a + 21);
|
||||
int64_t a9 = 2097151 & (load_4(a + 23) >> 5);
|
||||
int64_t a10 = 2097151 & (load_3(a + 26) >> 2);
|
||||
int64_t a11 = (load_4(a + 28) >> 7);
|
||||
int64_t b0 = 2097151 & load_3(b);
|
||||
int64_t b1 = 2097151 & (load_4(b + 2) >> 5);
|
||||
int64_t b2 = 2097151 & (load_3(b + 5) >> 2);
|
||||
int64_t b3 = 2097151 & (load_4(b + 7) >> 7);
|
||||
int64_t b4 = 2097151 & (load_4(b + 10) >> 4);
|
||||
int64_t b5 = 2097151 & (load_3(b + 13) >> 1);
|
||||
int64_t b6 = 2097151 & (load_4(b + 15) >> 6);
|
||||
int64_t b7 = 2097151 & (load_3(b + 18) >> 3);
|
||||
int64_t b8 = 2097151 & load_3(b + 21);
|
||||
int64_t b9 = 2097151 & (load_4(b + 23) >> 5);
|
||||
int64_t b10 = 2097151 & (load_3(b + 26) >> 2);
|
||||
int64_t b11 = (load_4(b + 28) >> 7);
|
||||
int64_t c0 = 2097151 & load_3(c);
|
||||
int64_t c1 = 2097151 & (load_4(c + 2) >> 5);
|
||||
int64_t c2 = 2097151 & (load_3(c + 5) >> 2);
|
||||
int64_t c3 = 2097151 & (load_4(c + 7) >> 7);
|
||||
int64_t c4 = 2097151 & (load_4(c + 10) >> 4);
|
||||
int64_t c5 = 2097151 & (load_3(c + 13) >> 1);
|
||||
int64_t c6 = 2097151 & (load_4(c + 15) >> 6);
|
||||
int64_t c7 = 2097151 & (load_3(c + 18) >> 3);
|
||||
int64_t c8 = 2097151 & load_3(c + 21);
|
||||
int64_t c9 = 2097151 & (load_4(c + 23) >> 5);
|
||||
int64_t c10 = 2097151 & (load_3(c + 26) >> 2);
|
||||
int64_t c11 = (load_4(c + 28) >> 7);
|
||||
int64_t s0;
|
||||
int64_t s1;
|
||||
int64_t s2;
|
||||
int64_t s3;
|
||||
int64_t s4;
|
||||
int64_t s5;
|
||||
int64_t s6;
|
||||
int64_t s7;
|
||||
int64_t s8;
|
||||
int64_t s9;
|
||||
int64_t s10;
|
||||
int64_t s11;
|
||||
int64_t s12;
|
||||
int64_t s13;
|
||||
int64_t s14;
|
||||
int64_t s15;
|
||||
int64_t s16;
|
||||
int64_t s17;
|
||||
int64_t s18;
|
||||
int64_t s19;
|
||||
int64_t s20;
|
||||
int64_t s21;
|
||||
int64_t s22;
|
||||
int64_t s23;
|
||||
int64_t carry0;
|
||||
int64_t carry1;
|
||||
int64_t carry2;
|
||||
int64_t carry3;
|
||||
int64_t carry4;
|
||||
int64_t carry5;
|
||||
int64_t carry6;
|
||||
int64_t carry7;
|
||||
int64_t carry8;
|
||||
int64_t carry9;
|
||||
int64_t carry10;
|
||||
int64_t carry11;
|
||||
int64_t carry12;
|
||||
int64_t carry13;
|
||||
int64_t carry14;
|
||||
int64_t carry15;
|
||||
int64_t carry16;
|
||||
int64_t carry17;
|
||||
int64_t carry18;
|
||||
int64_t carry19;
|
||||
int64_t carry20;
|
||||
int64_t carry21;
|
||||
int64_t carry22;
|
||||
|
||||
s0 = c0 + a0 * b0;
|
||||
s1 = c1 + a0 * b1 + a1 * b0;
|
||||
s2 = c2 + a0 * b2 + a1 * b1 + a2 * b0;
|
||||
s3 = c3 + a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0;
|
||||
s4 = c4 + a0 * b4 + a1 * b3 + a2 * b2 + a3 * b1 + a4 * b0;
|
||||
s5 = c5 + a0 * b5 + a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1 + a5 * b0;
|
||||
s6 = c6 + a0 * b6 + a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1 + a6 * b0;
|
||||
s7 = c7 + a0 * b7 + a1 * b6 + a2 * b5 + a3 * b4 + a4 * b3 + a5 * b2 + a6 * b1 + a7 * b0;
|
||||
s8 = c8 + a0 * b8 + a1 * b7 + a2 * b6 + a3 * b5 + a4 * b4 + a5 * b3 + a6 * b2 + a7 * b1 + a8 * b0;
|
||||
s9 = c9 + a0 * b9 + a1 * b8 + a2 * b7 + a3 * b6 + a4 * b5 + a5 * b4 + a6 * b3 + a7 * b2 + a8 * b1 + a9 * b0;
|
||||
s10 = c10 + a0 * b10 + a1 * b9 + a2 * b8 + a3 * b7 + a4 * b6 + a5 * b5 + a6 * b4 + a7 * b3 + a8 * b2 + a9 * b1 + a10 * b0;
|
||||
s11 = c11 + a0 * b11 + a1 * b10 + a2 * b9 + a3 * b8 + a4 * b7 + a5 * b6 + a6 * b5 + a7 * b4 + a8 * b3 + a9 * b2 + a10 * b1 + a11 * b0;
|
||||
s12 = a1 * b11 + a2 * b10 + a3 * b9 + a4 * b8 + a5 * b7 + a6 * b6 + a7 * b5 + a8 * b4 + a9 * b3 + a10 * b2 + a11 * b1;
|
||||
s13 = a2 * b11 + a3 * b10 + a4 * b9 + a5 * b8 + a6 * b7 + a7 * b6 + a8 * b5 + a9 * b4 + a10 * b3 + a11 * b2;
|
||||
s14 = a3 * b11 + a4 * b10 + a5 * b9 + a6 * b8 + a7 * b7 + a8 * b6 + a9 * b5 + a10 * b4 + a11 * b3;
|
||||
s15 = a4 * b11 + a5 * b10 + a6 * b9 + a7 * b8 + a8 * b7 + a9 * b6 + a10 * b5 + a11 * b4;
|
||||
s16 = a5 * b11 + a6 * b10 + a7 * b9 + a8 * b8 + a9 * b7 + a10 * b6 + a11 * b5;
|
||||
s17 = a6 * b11 + a7 * b10 + a8 * b9 + a9 * b8 + a10 * b7 + a11 * b6;
|
||||
s18 = a7 * b11 + a8 * b10 + a9 * b9 + a10 * b8 + a11 * b7;
|
||||
s19 = a8 * b11 + a9 * b10 + a10 * b9 + a11 * b8;
|
||||
s20 = a9 * b11 + a10 * b10 + a11 * b9;
|
||||
s21 = a10 * b11 + a11 * b10;
|
||||
s22 = a11 * b11;
|
||||
s23 = 0;
|
||||
carry0 = (s0 + (1 << 20)) >> 21;
|
||||
s1 += carry0;
|
||||
s0 -= carry0 << 21;
|
||||
carry2 = (s2 + (1 << 20)) >> 21;
|
||||
s3 += carry2;
|
||||
s2 -= carry2 << 21;
|
||||
carry4 = (s4 + (1 << 20)) >> 21;
|
||||
s5 += carry4;
|
||||
s4 -= carry4 << 21;
|
||||
carry6 = (s6 + (1 << 20)) >> 21;
|
||||
s7 += carry6;
|
||||
s6 -= carry6 << 21;
|
||||
carry8 = (s8 + (1 << 20)) >> 21;
|
||||
s9 += carry8;
|
||||
s8 -= carry8 << 21;
|
||||
carry10 = (s10 + (1 << 20)) >> 21;
|
||||
s11 += carry10;
|
||||
s10 -= carry10 << 21;
|
||||
carry12 = (s12 + (1 << 20)) >> 21;
|
||||
s13 += carry12;
|
||||
s12 -= carry12 << 21;
|
||||
carry14 = (s14 + (1 << 20)) >> 21;
|
||||
s15 += carry14;
|
||||
s14 -= carry14 << 21;
|
||||
carry16 = (s16 + (1 << 20)) >> 21;
|
||||
s17 += carry16;
|
||||
s16 -= carry16 << 21;
|
||||
carry18 = (s18 + (1 << 20)) >> 21;
|
||||
s19 += carry18;
|
||||
s18 -= carry18 << 21;
|
||||
carry20 = (s20 + (1 << 20)) >> 21;
|
||||
s21 += carry20;
|
||||
s20 -= carry20 << 21;
|
||||
carry22 = (s22 + (1 << 20)) >> 21;
|
||||
s23 += carry22;
|
||||
s22 -= carry22 << 21;
|
||||
carry1 = (s1 + (1 << 20)) >> 21;
|
||||
s2 += carry1;
|
||||
s1 -= carry1 << 21;
|
||||
carry3 = (s3 + (1 << 20)) >> 21;
|
||||
s4 += carry3;
|
||||
s3 -= carry3 << 21;
|
||||
carry5 = (s5 + (1 << 20)) >> 21;
|
||||
s6 += carry5;
|
||||
s5 -= carry5 << 21;
|
||||
carry7 = (s7 + (1 << 20)) >> 21;
|
||||
s8 += carry7;
|
||||
s7 -= carry7 << 21;
|
||||
carry9 = (s9 + (1 << 20)) >> 21;
|
||||
s10 += carry9;
|
||||
s9 -= carry9 << 21;
|
||||
carry11 = (s11 + (1 << 20)) >> 21;
|
||||
s12 += carry11;
|
||||
s11 -= carry11 << 21;
|
||||
carry13 = (s13 + (1 << 20)) >> 21;
|
||||
s14 += carry13;
|
||||
s13 -= carry13 << 21;
|
||||
carry15 = (s15 + (1 << 20)) >> 21;
|
||||
s16 += carry15;
|
||||
s15 -= carry15 << 21;
|
||||
carry17 = (s17 + (1 << 20)) >> 21;
|
||||
s18 += carry17;
|
||||
s17 -= carry17 << 21;
|
||||
carry19 = (s19 + (1 << 20)) >> 21;
|
||||
s20 += carry19;
|
||||
s19 -= carry19 << 21;
|
||||
carry21 = (s21 + (1 << 20)) >> 21;
|
||||
s22 += carry21;
|
||||
s21 -= carry21 << 21;
|
||||
s11 += s23 * 666643;
|
||||
s12 += s23 * 470296;
|
||||
s13 += s23 * 654183;
|
||||
s14 -= s23 * 997805;
|
||||
s15 += s23 * 136657;
|
||||
s16 -= s23 * 683901;
|
||||
s23 = 0;
|
||||
s10 += s22 * 666643;
|
||||
s11 += s22 * 470296;
|
||||
s12 += s22 * 654183;
|
||||
s13 -= s22 * 997805;
|
||||
s14 += s22 * 136657;
|
||||
s15 -= s22 * 683901;
|
||||
s22 = 0;
|
||||
s9 += s21 * 666643;
|
||||
s10 += s21 * 470296;
|
||||
s11 += s21 * 654183;
|
||||
s12 -= s21 * 997805;
|
||||
s13 += s21 * 136657;
|
||||
s14 -= s21 * 683901;
|
||||
s21 = 0;
|
||||
s8 += s20 * 666643;
|
||||
s9 += s20 * 470296;
|
||||
s10 += s20 * 654183;
|
||||
s11 -= s20 * 997805;
|
||||
s12 += s20 * 136657;
|
||||
s13 -= s20 * 683901;
|
||||
s20 = 0;
|
||||
s7 += s19 * 666643;
|
||||
s8 += s19 * 470296;
|
||||
s9 += s19 * 654183;
|
||||
s10 -= s19 * 997805;
|
||||
s11 += s19 * 136657;
|
||||
s12 -= s19 * 683901;
|
||||
s19 = 0;
|
||||
s6 += s18 * 666643;
|
||||
s7 += s18 * 470296;
|
||||
s8 += s18 * 654183;
|
||||
s9 -= s18 * 997805;
|
||||
s10 += s18 * 136657;
|
||||
s11 -= s18 * 683901;
|
||||
s18 = 0;
|
||||
carry6 = (s6 + (1 << 20)) >> 21;
|
||||
s7 += carry6;
|
||||
s6 -= carry6 << 21;
|
||||
carry8 = (s8 + (1 << 20)) >> 21;
|
||||
s9 += carry8;
|
||||
s8 -= carry8 << 21;
|
||||
carry10 = (s10 + (1 << 20)) >> 21;
|
||||
s11 += carry10;
|
||||
s10 -= carry10 << 21;
|
||||
carry12 = (s12 + (1 << 20)) >> 21;
|
||||
s13 += carry12;
|
||||
s12 -= carry12 << 21;
|
||||
carry14 = (s14 + (1 << 20)) >> 21;
|
||||
s15 += carry14;
|
||||
s14 -= carry14 << 21;
|
||||
carry16 = (s16 + (1 << 20)) >> 21;
|
||||
s17 += carry16;
|
||||
s16 -= carry16 << 21;
|
||||
carry7 = (s7 + (1 << 20)) >> 21;
|
||||
s8 += carry7;
|
||||
s7 -= carry7 << 21;
|
||||
carry9 = (s9 + (1 << 20)) >> 21;
|
||||
s10 += carry9;
|
||||
s9 -= carry9 << 21;
|
||||
carry11 = (s11 + (1 << 20)) >> 21;
|
||||
s12 += carry11;
|
||||
s11 -= carry11 << 21;
|
||||
carry13 = (s13 + (1 << 20)) >> 21;
|
||||
s14 += carry13;
|
||||
s13 -= carry13 << 21;
|
||||
carry15 = (s15 + (1 << 20)) >> 21;
|
||||
s16 += carry15;
|
||||
s15 -= carry15 << 21;
|
||||
s5 += s17 * 666643;
|
||||
s6 += s17 * 470296;
|
||||
s7 += s17 * 654183;
|
||||
s8 -= s17 * 997805;
|
||||
s9 += s17 * 136657;
|
||||
s10 -= s17 * 683901;
|
||||
s17 = 0;
|
||||
s4 += s16 * 666643;
|
||||
s5 += s16 * 470296;
|
||||
s6 += s16 * 654183;
|
||||
s7 -= s16 * 997805;
|
||||
s8 += s16 * 136657;
|
||||
s9 -= s16 * 683901;
|
||||
s16 = 0;
|
||||
s3 += s15 * 666643;
|
||||
s4 += s15 * 470296;
|
||||
s5 += s15 * 654183;
|
||||
s6 -= s15 * 997805;
|
||||
s7 += s15 * 136657;
|
||||
s8 -= s15 * 683901;
|
||||
s15 = 0;
|
||||
s2 += s14 * 666643;
|
||||
s3 += s14 * 470296;
|
||||
s4 += s14 * 654183;
|
||||
s5 -= s14 * 997805;
|
||||
s6 += s14 * 136657;
|
||||
s7 -= s14 * 683901;
|
||||
s14 = 0;
|
||||
s1 += s13 * 666643;
|
||||
s2 += s13 * 470296;
|
||||
s3 += s13 * 654183;
|
||||
s4 -= s13 * 997805;
|
||||
s5 += s13 * 136657;
|
||||
s6 -= s13 * 683901;
|
||||
s13 = 0;
|
||||
s0 += s12 * 666643;
|
||||
s1 += s12 * 470296;
|
||||
s2 += s12 * 654183;
|
||||
s3 -= s12 * 997805;
|
||||
s4 += s12 * 136657;
|
||||
s5 -= s12 * 683901;
|
||||
s12 = 0;
|
||||
carry0 = (s0 + (1 << 20)) >> 21;
|
||||
s1 += carry0;
|
||||
s0 -= carry0 << 21;
|
||||
carry2 = (s2 + (1 << 20)) >> 21;
|
||||
s3 += carry2;
|
||||
s2 -= carry2 << 21;
|
||||
carry4 = (s4 + (1 << 20)) >> 21;
|
||||
s5 += carry4;
|
||||
s4 -= carry4 << 21;
|
||||
carry6 = (s6 + (1 << 20)) >> 21;
|
||||
s7 += carry6;
|
||||
s6 -= carry6 << 21;
|
||||
carry8 = (s8 + (1 << 20)) >> 21;
|
||||
s9 += carry8;
|
||||
s8 -= carry8 << 21;
|
||||
carry10 = (s10 + (1 << 20)) >> 21;
|
||||
s11 += carry10;
|
||||
s10 -= carry10 << 21;
|
||||
carry1 = (s1 + (1 << 20)) >> 21;
|
||||
s2 += carry1;
|
||||
s1 -= carry1 << 21;
|
||||
carry3 = (s3 + (1 << 20)) >> 21;
|
||||
s4 += carry3;
|
||||
s3 -= carry3 << 21;
|
||||
carry5 = (s5 + (1 << 20)) >> 21;
|
||||
s6 += carry5;
|
||||
s5 -= carry5 << 21;
|
||||
carry7 = (s7 + (1 << 20)) >> 21;
|
||||
s8 += carry7;
|
||||
s7 -= carry7 << 21;
|
||||
carry9 = (s9 + (1 << 20)) >> 21;
|
||||
s10 += carry9;
|
||||
s9 -= carry9 << 21;
|
||||
carry11 = (s11 + (1 << 20)) >> 21;
|
||||
s12 += carry11;
|
||||
s11 -= carry11 << 21;
|
||||
s0 += s12 * 666643;
|
||||
s1 += s12 * 470296;
|
||||
s2 += s12 * 654183;
|
||||
s3 -= s12 * 997805;
|
||||
s4 += s12 * 136657;
|
||||
s5 -= s12 * 683901;
|
||||
s12 = 0;
|
||||
carry0 = s0 >> 21;
|
||||
s1 += carry0;
|
||||
s0 -= carry0 << 21;
|
||||
carry1 = s1 >> 21;
|
||||
s2 += carry1;
|
||||
s1 -= carry1 << 21;
|
||||
carry2 = s2 >> 21;
|
||||
s3 += carry2;
|
||||
s2 -= carry2 << 21;
|
||||
carry3 = s3 >> 21;
|
||||
s4 += carry3;
|
||||
s3 -= carry3 << 21;
|
||||
carry4 = s4 >> 21;
|
||||
s5 += carry4;
|
||||
s4 -= carry4 << 21;
|
||||
carry5 = s5 >> 21;
|
||||
s6 += carry5;
|
||||
s5 -= carry5 << 21;
|
||||
carry6 = s6 >> 21;
|
||||
s7 += carry6;
|
||||
s6 -= carry6 << 21;
|
||||
carry7 = s7 >> 21;
|
||||
s8 += carry7;
|
||||
s7 -= carry7 << 21;
|
||||
carry8 = s8 >> 21;
|
||||
s9 += carry8;
|
||||
s8 -= carry8 << 21;
|
||||
carry9 = s9 >> 21;
|
||||
s10 += carry9;
|
||||
s9 -= carry9 << 21;
|
||||
carry10 = s10 >> 21;
|
||||
s11 += carry10;
|
||||
s10 -= carry10 << 21;
|
||||
carry11 = s11 >> 21;
|
||||
s12 += carry11;
|
||||
s11 -= carry11 << 21;
|
||||
s0 += s12 * 666643;
|
||||
s1 += s12 * 470296;
|
||||
s2 += s12 * 654183;
|
||||
s3 -= s12 * 997805;
|
||||
s4 += s12 * 136657;
|
||||
s5 -= s12 * 683901;
|
||||
s12 = 0;
|
||||
carry0 = s0 >> 21;
|
||||
s1 += carry0;
|
||||
s0 -= carry0 << 21;
|
||||
carry1 = s1 >> 21;
|
||||
s2 += carry1;
|
||||
s1 -= carry1 << 21;
|
||||
carry2 = s2 >> 21;
|
||||
s3 += carry2;
|
||||
s2 -= carry2 << 21;
|
||||
carry3 = s3 >> 21;
|
||||
s4 += carry3;
|
||||
s3 -= carry3 << 21;
|
||||
carry4 = s4 >> 21;
|
||||
s5 += carry4;
|
||||
s4 -= carry4 << 21;
|
||||
carry5 = s5 >> 21;
|
||||
s6 += carry5;
|
||||
s5 -= carry5 << 21;
|
||||
carry6 = s6 >> 21;
|
||||
s7 += carry6;
|
||||
s6 -= carry6 << 21;
|
||||
carry7 = s7 >> 21;
|
||||
s8 += carry7;
|
||||
s7 -= carry7 << 21;
|
||||
carry8 = s8 >> 21;
|
||||
s9 += carry8;
|
||||
s8 -= carry8 << 21;
|
||||
carry9 = s9 >> 21;
|
||||
s10 += carry9;
|
||||
s9 -= carry9 << 21;
|
||||
carry10 = s10 >> 21;
|
||||
s11 += carry10;
|
||||
s10 -= carry10 << 21;
|
||||
|
||||
s[0] = (unsigned char) (s0 >> 0);
|
||||
s[1] = (unsigned char) (s0 >> 8);
|
||||
s[2] = (unsigned char) ((s0 >> 16) | (s1 << 5));
|
||||
s[3] = (unsigned char) (s1 >> 3);
|
||||
s[4] = (unsigned char) (s1 >> 11);
|
||||
s[5] = (unsigned char) ((s1 >> 19) | (s2 << 2));
|
||||
s[6] = (unsigned char) (s2 >> 6);
|
||||
s[7] = (unsigned char) ((s2 >> 14) | (s3 << 7));
|
||||
s[8] = (unsigned char) (s3 >> 1);
|
||||
s[9] = (unsigned char) (s3 >> 9);
|
||||
s[10] = (unsigned char) ((s3 >> 17) | (s4 << 4));
|
||||
s[11] = (unsigned char) (s4 >> 4);
|
||||
s[12] = (unsigned char) (s4 >> 12);
|
||||
s[13] = (unsigned char) ((s4 >> 20) | (s5 << 1));
|
||||
s[14] = (unsigned char) (s5 >> 7);
|
||||
s[15] = (unsigned char) ((s5 >> 15) | (s6 << 6));
|
||||
s[16] = (unsigned char) (s6 >> 2);
|
||||
s[17] = (unsigned char) (s6 >> 10);
|
||||
s[18] = (unsigned char) ((s6 >> 18) | (s7 << 3));
|
||||
s[19] = (unsigned char) (s7 >> 5);
|
||||
s[20] = (unsigned char) (s7 >> 13);
|
||||
s[21] = (unsigned char) (s8 >> 0);
|
||||
s[22] = (unsigned char) (s8 >> 8);
|
||||
s[23] = (unsigned char) ((s8 >> 16) | (s9 << 5));
|
||||
s[24] = (unsigned char) (s9 >> 3);
|
||||
s[25] = (unsigned char) (s9 >> 11);
|
||||
s[26] = (unsigned char) ((s9 >> 19) | (s10 << 2));
|
||||
s[27] = (unsigned char) (s10 >> 6);
|
||||
s[28] = (unsigned char) ((s10 >> 14) | (s11 << 7));
|
||||
s[29] = (unsigned char) (s11 >> 1);
|
||||
s[30] = (unsigned char) (s11 >> 9);
|
||||
s[31] = (unsigned char) (s11 >> 17);
|
||||
}
|
||||
12
lib/ed25519/sc.h
Executable file
12
lib/ed25519/sc.h
Executable file
@@ -0,0 +1,12 @@
|
||||
#ifndef SC_H
|
||||
#define SC_H
|
||||
|
||||
/*
|
||||
The set of scalars is \Z/l
|
||||
where l = 2^252 + 27742317777372353535851937790883648493.
|
||||
*/
|
||||
|
||||
void sc_reduce(unsigned char *s);
|
||||
void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c);
|
||||
|
||||
#endif
|
||||
40
lib/ed25519/seed.c
Executable file
40
lib/ed25519/seed.c
Executable file
@@ -0,0 +1,40 @@
|
||||
#include "ed25519.h"
|
||||
|
||||
#ifndef ED25519_NO_SEED
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <wincrypt.h>
|
||||
#else
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
int ed25519_create_seed(unsigned char *seed) {
|
||||
#ifdef _WIN32
|
||||
HCRYPTPROV prov;
|
||||
|
||||
if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!CryptGenRandom(prov, 32, seed)) {
|
||||
CryptReleaseContext(prov, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
CryptReleaseContext(prov, 0);
|
||||
#else
|
||||
FILE *f = fopen("/dev/urandom", "rb");
|
||||
|
||||
if (f == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
fread(seed, 1, 32, f);
|
||||
fclose(f);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
275
lib/ed25519/sha512.c
Executable file
275
lib/ed25519/sha512.c
Executable file
@@ -0,0 +1,275 @@
|
||||
/* LibTomCrypt, modular cryptographic library -- Tom St Denis
|
||||
*
|
||||
* LibTomCrypt is a library that provides various cryptographic
|
||||
* algorithms in a highly modular and flexible manner.
|
||||
*
|
||||
* The library is free for all purposes without any express
|
||||
* guarantee it works.
|
||||
*
|
||||
* Tom St Denis, tomstdenis@gmail.com, http://libtom.org
|
||||
*/
|
||||
|
||||
#include "fixedint.h"
|
||||
#include "sha512.h"
|
||||
|
||||
/* the K array */
|
||||
static const uint64_t K[80] = {
|
||||
UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd),
|
||||
UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc),
|
||||
UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019),
|
||||
UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118),
|
||||
UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe),
|
||||
UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2),
|
||||
UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1),
|
||||
UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694),
|
||||
UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3),
|
||||
UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65),
|
||||
UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483),
|
||||
UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5),
|
||||
UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210),
|
||||
UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4),
|
||||
UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725),
|
||||
UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70),
|
||||
UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926),
|
||||
UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df),
|
||||
UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8),
|
||||
UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b),
|
||||
UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001),
|
||||
UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30),
|
||||
UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910),
|
||||
UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8),
|
||||
UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53),
|
||||
UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8),
|
||||
UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb),
|
||||
UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3),
|
||||
UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60),
|
||||
UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec),
|
||||
UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9),
|
||||
UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b),
|
||||
UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207),
|
||||
UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178),
|
||||
UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6),
|
||||
UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b),
|
||||
UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493),
|
||||
UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c),
|
||||
UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a),
|
||||
UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817)
|
||||
};
|
||||
|
||||
/* Various logical functions */
|
||||
|
||||
#define ROR64c(x, y) \
|
||||
( ((((x)&UINT64_C(0xFFFFFFFFFFFFFFFF))>>((uint64_t)(y)&UINT64_C(63))) | \
|
||||
((x)<<((uint64_t)(64-((y)&UINT64_C(63)))))) & UINT64_C(0xFFFFFFFFFFFFFFFF))
|
||||
|
||||
#define STORE64H(x, y) \
|
||||
{ (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \
|
||||
(y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \
|
||||
(y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \
|
||||
(y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); }
|
||||
|
||||
#define LOAD64H(x, y) \
|
||||
{ x = (((uint64_t)((y)[0] & 255))<<56)|(((uint64_t)((y)[1] & 255))<<48) | \
|
||||
(((uint64_t)((y)[2] & 255))<<40)|(((uint64_t)((y)[3] & 255))<<32) | \
|
||||
(((uint64_t)((y)[4] & 255))<<24)|(((uint64_t)((y)[5] & 255))<<16) | \
|
||||
(((uint64_t)((y)[6] & 255))<<8)|(((uint64_t)((y)[7] & 255))); }
|
||||
|
||||
|
||||
#define Ch(x,y,z) (z ^ (x & (y ^ z)))
|
||||
#define Maj(x,y,z) (((x | y) & z) | (x & y))
|
||||
#define S(x, n) ROR64c(x, n)
|
||||
#define R(x, n) (((x) &UINT64_C(0xFFFFFFFFFFFFFFFF))>>((uint64_t)n))
|
||||
#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39))
|
||||
#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41))
|
||||
#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7))
|
||||
#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6))
|
||||
#ifndef MIN
|
||||
#define MIN(x, y) ( ((x)<(y))?(x):(y) )
|
||||
#endif
|
||||
|
||||
/* compress 1024-bits */
|
||||
static int sha512_compress(sha512_context *md, unsigned char *buf)
|
||||
{
|
||||
uint64_t S[8], W[80], t0, t1;
|
||||
int i;
|
||||
|
||||
/* copy state into S */
|
||||
for (i = 0; i < 8; i++) {
|
||||
S[i] = md->state[i];
|
||||
}
|
||||
|
||||
/* copy the state into 1024-bits into W[0..15] */
|
||||
for (i = 0; i < 16; i++) {
|
||||
LOAD64H(W[i], buf + (8*i));
|
||||
}
|
||||
|
||||
/* fill W[16..79] */
|
||||
for (i = 16; i < 80; i++) {
|
||||
W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
|
||||
}
|
||||
|
||||
/* Compress */
|
||||
#define RND(a,b,c,d,e,f,g,h,i) \
|
||||
t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \
|
||||
t1 = Sigma0(a) + Maj(a, b, c);\
|
||||
d += t0; \
|
||||
h = t0 + t1;
|
||||
|
||||
for (i = 0; i < 80; i += 8) {
|
||||
RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0);
|
||||
RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1);
|
||||
RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2);
|
||||
RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3);
|
||||
RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4);
|
||||
RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5);
|
||||
RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6);
|
||||
RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7);
|
||||
}
|
||||
|
||||
#undef RND
|
||||
|
||||
|
||||
|
||||
/* feedback */
|
||||
for (i = 0; i < 8; i++) {
|
||||
md->state[i] = md->state[i] + S[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Initialize the hash state
|
||||
@param md The hash state you wish to initialize
|
||||
@return 0 if successful
|
||||
*/
|
||||
int sha512_init(sha512_context * md) {
|
||||
if (md == NULL) return 1;
|
||||
|
||||
md->curlen = 0;
|
||||
md->length = 0;
|
||||
md->state[0] = UINT64_C(0x6a09e667f3bcc908);
|
||||
md->state[1] = UINT64_C(0xbb67ae8584caa73b);
|
||||
md->state[2] = UINT64_C(0x3c6ef372fe94f82b);
|
||||
md->state[3] = UINT64_C(0xa54ff53a5f1d36f1);
|
||||
md->state[4] = UINT64_C(0x510e527fade682d1);
|
||||
md->state[5] = UINT64_C(0x9b05688c2b3e6c1f);
|
||||
md->state[6] = UINT64_C(0x1f83d9abfb41bd6b);
|
||||
md->state[7] = UINT64_C(0x5be0cd19137e2179);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
Process a block of memory though the hash
|
||||
@param md The hash state
|
||||
@param in The data to hash
|
||||
@param inlen The length of the data (octets)
|
||||
@return 0 if successful
|
||||
*/
|
||||
int sha512_update (sha512_context * md, const unsigned char *in, size_t inlen)
|
||||
{
|
||||
size_t n;
|
||||
size_t i;
|
||||
int err;
|
||||
if (md == NULL) return 1;
|
||||
if (in == NULL) return 1;
|
||||
if (md->curlen > sizeof(md->buf)) {
|
||||
return 1;
|
||||
}
|
||||
while (inlen > 0) {
|
||||
if (md->curlen == 0 && inlen >= 128) {
|
||||
if ((err = sha512_compress (md, (unsigned char *)in)) != 0) {
|
||||
return err;
|
||||
}
|
||||
md->length += 128 * 8;
|
||||
in += 128;
|
||||
inlen -= 128;
|
||||
} else {
|
||||
n = MIN(inlen, (128 - md->curlen));
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
md->buf[i + md->curlen] = in[i];
|
||||
}
|
||||
|
||||
|
||||
md->curlen += n;
|
||||
in += n;
|
||||
inlen -= n;
|
||||
if (md->curlen == 128) {
|
||||
if ((err = sha512_compress (md, md->buf)) != 0) {
|
||||
return err;
|
||||
}
|
||||
md->length += 8*128;
|
||||
md->curlen = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
Terminate the hash to get the digest
|
||||
@param md The hash state
|
||||
@param out [out] The destination of the hash (64 bytes)
|
||||
@return 0 if successful
|
||||
*/
|
||||
int sha512_final(sha512_context * md, unsigned char *out)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (md == NULL) return 1;
|
||||
if (out == NULL) return 1;
|
||||
|
||||
if (md->curlen >= sizeof(md->buf)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* increase the length of the message */
|
||||
md->length += md->curlen * UINT64_C(8);
|
||||
|
||||
/* append the '1' bit */
|
||||
md->buf[md->curlen++] = (unsigned char)0x80;
|
||||
|
||||
/* if the length is currently above 112 bytes we append zeros
|
||||
* then compress. Then we can fall back to padding zeros and length
|
||||
* encoding like normal.
|
||||
*/
|
||||
if (md->curlen > 112) {
|
||||
while (md->curlen < 128) {
|
||||
md->buf[md->curlen++] = (unsigned char)0;
|
||||
}
|
||||
sha512_compress(md, md->buf);
|
||||
md->curlen = 0;
|
||||
}
|
||||
|
||||
/* pad upto 120 bytes of zeroes
|
||||
* note: that from 112 to 120 is the 64 MSB of the length. We assume that you won't hash
|
||||
* > 2^64 bits of data... :-)
|
||||
*/
|
||||
while (md->curlen < 120) {
|
||||
md->buf[md->curlen++] = (unsigned char)0;
|
||||
}
|
||||
|
||||
/* store length */
|
||||
STORE64H(md->length, md->buf+120);
|
||||
sha512_compress(md, md->buf);
|
||||
|
||||
/* copy output */
|
||||
for (i = 0; i < 8; i++) {
|
||||
STORE64H(md->state[i], out+(8*i));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sha512(const unsigned char *message, size_t message_len, unsigned char *out)
|
||||
{
|
||||
sha512_context ctx;
|
||||
int ret;
|
||||
if ((ret = sha512_init(&ctx))) return ret;
|
||||
if ((ret = sha512_update(&ctx, message, message_len))) return ret;
|
||||
if ((ret = sha512_final(&ctx, out))) return ret;
|
||||
return 0;
|
||||
}
|
||||
28
lib/ed25519/sha512.h
Executable file
28
lib/ed25519/sha512.h
Executable file
@@ -0,0 +1,28 @@
|
||||
#ifndef SHA512_H
|
||||
#define SHA512_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "fixedint.h"
|
||||
|
||||
/* state */
|
||||
typedef struct sha512_context_ {
|
||||
uint64_t length, state[8];
|
||||
size_t curlen;
|
||||
unsigned char buf[128];
|
||||
} sha512_context;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int sha512_init(sha512_context * md);
|
||||
int sha512_final(sha512_context * md, unsigned char *out);
|
||||
int sha512_update(sha512_context * md, const unsigned char *in, size_t inlen);
|
||||
int sha512(const unsigned char *message, size_t message_len, unsigned char *out);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
31
lib/ed25519/sign.c
Executable file
31
lib/ed25519/sign.c
Executable file
@@ -0,0 +1,31 @@
|
||||
#include "ed25519.h"
|
||||
#include "sha512.h"
|
||||
#include "ge.h"
|
||||
#include "sc.h"
|
||||
|
||||
|
||||
void ed25519_sign(unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key, const unsigned char *private_key) {
|
||||
sha512_context hash;
|
||||
unsigned char hram[64];
|
||||
unsigned char r[64];
|
||||
ge_p3 R;
|
||||
|
||||
|
||||
sha512_init(&hash);
|
||||
sha512_update(&hash, private_key + 32, 32);
|
||||
sha512_update(&hash, message, message_len);
|
||||
sha512_final(&hash, r);
|
||||
|
||||
sc_reduce(r);
|
||||
ge_scalarmult_base(&R, r);
|
||||
ge_p3_tobytes(signature, &R);
|
||||
|
||||
sha512_init(&hash);
|
||||
sha512_update(&hash, signature, 32);
|
||||
sha512_update(&hash, public_key, 32);
|
||||
sha512_update(&hash, message, message_len);
|
||||
sha512_final(&hash, hram);
|
||||
|
||||
sc_reduce(hram);
|
||||
sc_muladd(signature + 32, hram, private_key, r);
|
||||
}
|
||||
77
lib/ed25519/verify.c
Executable file
77
lib/ed25519/verify.c
Executable file
@@ -0,0 +1,77 @@
|
||||
#include "ed25519.h"
|
||||
#include "sha512.h"
|
||||
#include "ge.h"
|
||||
#include "sc.h"
|
||||
|
||||
static int consttime_equal(const unsigned char *x, const unsigned char *y) {
|
||||
unsigned char r = 0;
|
||||
|
||||
r = x[0] ^ y[0];
|
||||
#define F(i) r |= x[i] ^ y[i]
|
||||
F(1);
|
||||
F(2);
|
||||
F(3);
|
||||
F(4);
|
||||
F(5);
|
||||
F(6);
|
||||
F(7);
|
||||
F(8);
|
||||
F(9);
|
||||
F(10);
|
||||
F(11);
|
||||
F(12);
|
||||
F(13);
|
||||
F(14);
|
||||
F(15);
|
||||
F(16);
|
||||
F(17);
|
||||
F(18);
|
||||
F(19);
|
||||
F(20);
|
||||
F(21);
|
||||
F(22);
|
||||
F(23);
|
||||
F(24);
|
||||
F(25);
|
||||
F(26);
|
||||
F(27);
|
||||
F(28);
|
||||
F(29);
|
||||
F(30);
|
||||
F(31);
|
||||
#undef F
|
||||
|
||||
return !r;
|
||||
}
|
||||
|
||||
int ed25519_verify(const unsigned char *signature, const unsigned char *message, size_t message_len, const unsigned char *public_key) {
|
||||
unsigned char h[64];
|
||||
unsigned char checker[32];
|
||||
sha512_context hash;
|
||||
ge_p3 A;
|
||||
ge_p2 R;
|
||||
|
||||
if (signature[63] & 224) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ge_frombytes_negate_vartime(&A, public_key) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
sha512_init(&hash);
|
||||
sha512_update(&hash, signature, 32);
|
||||
sha512_update(&hash, public_key, 32);
|
||||
sha512_update(&hash, message, message_len);
|
||||
sha512_final(&hash, h);
|
||||
|
||||
sc_reduce(h);
|
||||
ge_double_scalarmult_vartime(&R, h, &A, signature + 32);
|
||||
ge_tobytes(checker, &R);
|
||||
|
||||
if (!consttime_equal(checker, signature)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
0
lib/fairplay.h
Normal file → Executable file
0
lib/fairplay.h
Normal file → Executable file
87
lib/fairplay_playfair.c
Executable file
87
lib/fairplay_playfair.c
Executable file
@@ -0,0 +1,87 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "fairplay.h"
|
||||
#include "playfair/playfair.h"
|
||||
|
||||
char reply_message[4][142] = {{0x46,0x50,0x4c,0x59,0x03,0x01,0x02,0x00,0x00,0x00,0x00,0x82,0x02,0x00,0x0f,0x9f,0x3f,0x9e,0x0a,0x25,0x21,0xdb,0xdf,0x31,0x2a,0xb2,0xbf,0xb2,0x9e,0x8d,0x23,0x2b,0x63,0x76,0xa8,0xc8,0x18,0x70,0x1d,0x22,0xae,0x93,0xd8,0x27,0x37,0xfe,0xaf,0x9d,0xb4,0xfd,0xf4,0x1c,0x2d,0xba,0x9d,0x1f,0x49,0xca,0xaa,0xbf,0x65,0x91,0xac,0x1f,0x7b,0xc6,0xf7,0xe0,0x66,0x3d,0x21,0xaf,0xe0,0x15,0x65,0x95,0x3e,0xab,0x81,0xf4,0x18,0xce,0xed,0x09,0x5a,0xdb,0x7c,0x3d,0x0e,0x25,0x49,0x09,0xa7,0x98,0x31,0xd4,0x9c,0x39,0x82,0x97,0x34,0x34,0xfa,0xcb,0x42,0xc6,0x3a,0x1c,0xd9,0x11,0xa6,0xfe,0x94,0x1a,0x8a,0x6d,0x4a,0x74,0x3b,0x46,0xc3,0xa7,0x64,0x9e,0x44,0xc7,0x89,0x55,0xe4,0x9d,0x81,0x55,0x00,0x95,0x49,0xc4,0xe2,0xf7,0xa3,0xf6,0xd5,0xba},
|
||||
{0x46,0x50,0x4c,0x59,0x03,0x01,0x02,0x00,0x00,0x00,0x00,0x82,0x02,0x01,0xcf,0x32,0xa2,0x57,0x14,0xb2,0x52,0x4f,0x8a,0xa0,0xad,0x7a,0xf1,0x64,0xe3,0x7b,0xcf,0x44,0x24,0xe2,0x00,0x04,0x7e,0xfc,0x0a,0xd6,0x7a,0xfc,0xd9,0x5d,0xed,0x1c,0x27,0x30,0xbb,0x59,0x1b,0x96,0x2e,0xd6,0x3a,0x9c,0x4d,0xed,0x88,0xba,0x8f,0xc7,0x8d,0xe6,0x4d,0x91,0xcc,0xfd,0x5c,0x7b,0x56,0xda,0x88,0xe3,0x1f,0x5c,0xce,0xaf,0xc7,0x43,0x19,0x95,0xa0,0x16,0x65,0xa5,0x4e,0x19,0x39,0xd2,0x5b,0x94,0xdb,0x64,0xb9,0xe4,0x5d,0x8d,0x06,0x3e,0x1e,0x6a,0xf0,0x7e,0x96,0x56,0x16,0x2b,0x0e,0xfa,0x40,0x42,0x75,0xea,0x5a,0x44,0xd9,0x59,0x1c,0x72,0x56,0xb9,0xfb,0xe6,0x51,0x38,0x98,0xb8,0x02,0x27,0x72,0x19,0x88,0x57,0x16,0x50,0x94,0x2a,0xd9,0x46,0x68,0x8a},
|
||||
{0x46,0x50,0x4c,0x59,0x03,0x01,0x02,0x00,0x00,0x00,0x00,0x82,0x02,0x02,0xc1,0x69,0xa3,0x52,0xee,0xed,0x35,0xb1,0x8c,0xdd,0x9c,0x58,0xd6,0x4f,0x16,0xc1,0x51,0x9a,0x89,0xeb,0x53,0x17,0xbd,0x0d,0x43,0x36,0xcd,0x68,0xf6,0x38,0xff,0x9d,0x01,0x6a,0x5b,0x52,0xb7,0xfa,0x92,0x16,0xb2,0xb6,0x54,0x82,0xc7,0x84,0x44,0x11,0x81,0x21,0xa2,0xc7,0xfe,0xd8,0x3d,0xb7,0x11,0x9e,0x91,0x82,0xaa,0xd7,0xd1,0x8c,0x70,0x63,0xe2,0xa4,0x57,0x55,0x59,0x10,0xaf,0x9e,0x0e,0xfc,0x76,0x34,0x7d,0x16,0x40,0x43,0x80,0x7f,0x58,0x1e,0xe4,0xfb,0xe4,0x2c,0xa9,0xde,0xdc,0x1b,0x5e,0xb2,0xa3,0xaa,0x3d,0x2e,0xcd,0x59,0xe7,0xee,0xe7,0x0b,0x36,0x29,0xf2,0x2a,0xfd,0x16,0x1d,0x87,0x73,0x53,0xdd,0xb9,0x9a,0xdc,0x8e,0x07,0x00,0x6e,0x56,0xf8,0x50,0xce},
|
||||
{0x46,0x50,0x4c,0x59,0x03,0x01,0x02,0x00,0x00,0x00,0x00,0x82,0x02,0x03,0x90,0x01,0xe1,0x72,0x7e,0x0f,0x57,0xf9,0xf5,0x88,0x0d,0xb1,0x04,0xa6,0x25,0x7a,0x23,0xf5,0xcf,0xff,0x1a,0xbb,0xe1,0xe9,0x30,0x45,0x25,0x1a,0xfb,0x97,0xeb,0x9f,0xc0,0x01,0x1e,0xbe,0x0f,0x3a,0x81,0xdf,0x5b,0x69,0x1d,0x76,0xac,0xb2,0xf7,0xa5,0xc7,0x08,0xe3,0xd3,0x28,0xf5,0x6b,0xb3,0x9d,0xbd,0xe5,0xf2,0x9c,0x8a,0x17,0xf4,0x81,0x48,0x7e,0x3a,0xe8,0x63,0xc6,0x78,0x32,0x54,0x22,0xe6,0xf7,0x8e,0x16,0x6d,0x18,0xaa,0x7f,0xd6,0x36,0x25,0x8b,0xce,0x28,0x72,0x6f,0x66,0x1f,0x73,0x88,0x93,0xce,0x44,0x31,0x1e,0x4b,0xe6,0xc0,0x53,0x51,0x93,0xe5,0xef,0x72,0xe8,0x68,0x62,0x33,0x72,0x9c,0x22,0x7d,0x82,0x0c,0x99,0x94,0x45,0xd8,0x92,0x46,0xc8,0xc3,0x59}};
|
||||
|
||||
char fp_header[] = {0x46, 0x50, 0x4c, 0x59, 0x03, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x14};
|
||||
|
||||
struct fairplay_s {
|
||||
logger_t *logger;
|
||||
|
||||
unsigned char keymsg[164];
|
||||
unsigned int keymsglen;
|
||||
};
|
||||
|
||||
fairplay_t *
|
||||
fairplay_init(logger_t *logger)
|
||||
{
|
||||
fairplay_t *fp;
|
||||
|
||||
fp = calloc(1, sizeof(fairplay_t));
|
||||
if (!fp) {
|
||||
return NULL;
|
||||
}
|
||||
fp->logger = logger;
|
||||
|
||||
return fp;
|
||||
}
|
||||
|
||||
int
|
||||
fairplay_setup(fairplay_t *fp, const unsigned char req[16], unsigned char res[142])
|
||||
{
|
||||
int mode;
|
||||
|
||||
assert(fp);
|
||||
|
||||
if (req[4] != 0x03) {
|
||||
/* Unsupported fairplay version */
|
||||
return -1;
|
||||
}
|
||||
|
||||
mode = req[14];
|
||||
memcpy(res, reply_message[mode], 142);
|
||||
fp->keymsglen = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fairplay_handshake(fairplay_t *fp, const unsigned char req[164], unsigned char res[32])
|
||||
{
|
||||
assert(fp);
|
||||
|
||||
if (req[4] != 0x03) {
|
||||
/* Unsupported fairplay version */
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(fp->keymsg, req, 164);
|
||||
fp->keymsglen = 164;
|
||||
|
||||
memcpy(res, fp_header, 12);
|
||||
memcpy(res + 12, req + 144, 20);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fairplay_decrypt(fairplay_t *fp, const unsigned char input[72], unsigned char output[16])
|
||||
{
|
||||
if (fp->keymsglen != 164) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
playfair_decrypt(fp->keymsg, (unsigned char *) input, output);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
fairplay_destroy(fairplay_t *fp)
|
||||
{
|
||||
free(fp);
|
||||
}
|
||||
0
lib/global.h
Normal file → Executable file
0
lib/global.h
Normal file → Executable file
2577
lib/http_parser.c
Executable file
2577
lib/http_parser.c
Executable file
File diff suppressed because it is too large
Load Diff
455
lib/http_parser.h
Executable file
455
lib/http_parser.h
Executable file
@@ -0,0 +1,455 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. 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.
|
||||
*/
|
||||
#ifndef http_parser_h
|
||||
#define http_parser_h
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Also update SONAME in the Makefile whenever you change these. */
|
||||
#define HTTP_PARSER_VERSION_MAJOR 2
|
||||
#define HTTP_PARSER_VERSION_MINOR 9
|
||||
#define HTTP_PARSER_VERSION_PATCH 3
|
||||
|
||||
#include <stddef.h>
|
||||
#if defined(_WIN32) && !defined(__MINGW32__) && \
|
||||
(!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
|
||||
#include <BaseTsd.h>
|
||||
typedef __int8 int8_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef __int16 int16_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef __int32 int32_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
|
||||
* faster
|
||||
*/
|
||||
#ifndef HTTP_PARSER_STRICT
|
||||
# define HTTP_PARSER_STRICT 0
|
||||
#endif
|
||||
|
||||
/* Maximium header size allowed. If the macro is not defined
|
||||
* before including this header then the default is used. To
|
||||
* change the maximum header size, define the macro in the build
|
||||
* environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
|
||||
* the effective limit on the size of the header, define the macro
|
||||
* to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
|
||||
*/
|
||||
#ifndef HTTP_MAX_HEADER_SIZE
|
||||
# define HTTP_MAX_HEADER_SIZE (80*1024)
|
||||
#endif
|
||||
|
||||
typedef struct http_parser http_parser;
|
||||
typedef struct http_parser_settings http_parser_settings;
|
||||
|
||||
|
||||
/* Callbacks should return non-zero to indicate an error. The parser will
|
||||
* then halt execution.
|
||||
*
|
||||
* The one exception is on_headers_complete. In a HTTP_RESPONSE parser
|
||||
* returning '1' from on_headers_complete will tell the parser that it
|
||||
* should not expect a body. This is used when receiving a response to a
|
||||
* HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
|
||||
* chunked' headers that indicate the presence of a body.
|
||||
*
|
||||
* Returning `2` from on_headers_complete will tell parser that it should not
|
||||
* expect neither a body nor any futher responses on this connection. This is
|
||||
* useful for handling responses to a CONNECT request which may not contain
|
||||
* `Upgrade` or `Connection: upgrade` headers.
|
||||
*
|
||||
* http_data_cb does not return data chunks. It will be called arbitrarily
|
||||
* many times for each string. E.G. you might get 10 callbacks for "on_url"
|
||||
* each providing just a few characters more data.
|
||||
*/
|
||||
typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
|
||||
typedef int (*http_cb) (http_parser*);
|
||||
|
||||
|
||||
/* Status Codes */
|
||||
#define HTTP_STATUS_MAP(XX) \
|
||||
XX(100, CONTINUE, Continue) \
|
||||
XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \
|
||||
XX(102, PROCESSING, Processing) \
|
||||
XX(200, OK, OK) \
|
||||
XX(201, CREATED, Created) \
|
||||
XX(202, ACCEPTED, Accepted) \
|
||||
XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \
|
||||
XX(204, NO_CONTENT, No Content) \
|
||||
XX(205, RESET_CONTENT, Reset Content) \
|
||||
XX(206, PARTIAL_CONTENT, Partial Content) \
|
||||
XX(207, MULTI_STATUS, Multi-Status) \
|
||||
XX(208, ALREADY_REPORTED, Already Reported) \
|
||||
XX(226, IM_USED, IM Used) \
|
||||
XX(300, MULTIPLE_CHOICES, Multiple Choices) \
|
||||
XX(301, MOVED_PERMANENTLY, Moved Permanently) \
|
||||
XX(302, FOUND, Found) \
|
||||
XX(303, SEE_OTHER, See Other) \
|
||||
XX(304, NOT_MODIFIED, Not Modified) \
|
||||
XX(305, USE_PROXY, Use Proxy) \
|
||||
XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \
|
||||
XX(308, PERMANENT_REDIRECT, Permanent Redirect) \
|
||||
XX(400, BAD_REQUEST, Bad Request) \
|
||||
XX(401, UNAUTHORIZED, Unauthorized) \
|
||||
XX(402, PAYMENT_REQUIRED, Payment Required) \
|
||||
XX(403, FORBIDDEN, Forbidden) \
|
||||
XX(404, NOT_FOUND, Not Found) \
|
||||
XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \
|
||||
XX(406, NOT_ACCEPTABLE, Not Acceptable) \
|
||||
XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \
|
||||
XX(408, REQUEST_TIMEOUT, Request Timeout) \
|
||||
XX(409, CONFLICT, Conflict) \
|
||||
XX(410, GONE, Gone) \
|
||||
XX(411, LENGTH_REQUIRED, Length Required) \
|
||||
XX(412, PRECONDITION_FAILED, Precondition Failed) \
|
||||
XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \
|
||||
XX(414, URI_TOO_LONG, URI Too Long) \
|
||||
XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \
|
||||
XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \
|
||||
XX(417, EXPECTATION_FAILED, Expectation Failed) \
|
||||
XX(421, MISDIRECTED_REQUEST, Misdirected Request) \
|
||||
XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \
|
||||
XX(423, LOCKED, Locked) \
|
||||
XX(424, FAILED_DEPENDENCY, Failed Dependency) \
|
||||
XX(426, UPGRADE_REQUIRED, Upgrade Required) \
|
||||
XX(428, PRECONDITION_REQUIRED, Precondition Required) \
|
||||
XX(429, TOO_MANY_REQUESTS, Too Many Requests) \
|
||||
XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \
|
||||
XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \
|
||||
XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \
|
||||
XX(501, NOT_IMPLEMENTED, Not Implemented) \
|
||||
XX(502, BAD_GATEWAY, Bad Gateway) \
|
||||
XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \
|
||||
XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \
|
||||
XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \
|
||||
XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \
|
||||
XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \
|
||||
XX(508, LOOP_DETECTED, Loop Detected) \
|
||||
XX(510, NOT_EXTENDED, Not Extended) \
|
||||
XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \
|
||||
|
||||
enum http_status
|
||||
{
|
||||
#define XX(num, name, string) HTTP_STATUS_##name = num,
|
||||
HTTP_STATUS_MAP(XX)
|
||||
#undef XX
|
||||
};
|
||||
|
||||
|
||||
/* Request Methods */
|
||||
#define HTTP_METHOD_MAP(XX) \
|
||||
XX(0, DELETE, DELETE) \
|
||||
XX(1, GET, GET) \
|
||||
XX(2, HEAD, HEAD) \
|
||||
XX(3, POST, POST) \
|
||||
XX(4, PUT, PUT) \
|
||||
/* pathological */ \
|
||||
XX(5, CONNECT, CONNECT) \
|
||||
XX(6, OPTIONS, OPTIONS) \
|
||||
XX(7, TRACE, TRACE) \
|
||||
/* WebDAV */ \
|
||||
XX(8, COPY, COPY) \
|
||||
XX(9, LOCK, LOCK) \
|
||||
XX(10, MKCOL, MKCOL) \
|
||||
XX(11, MOVE, MOVE) \
|
||||
XX(12, PROPFIND, PROPFIND) \
|
||||
XX(13, PROPPATCH, PROPPATCH) \
|
||||
XX(14, SEARCH, SEARCH) \
|
||||
XX(15, UNLOCK, UNLOCK) \
|
||||
XX(16, BIND, BIND) \
|
||||
XX(17, REBIND, REBIND) \
|
||||
XX(18, UNBIND, UNBIND) \
|
||||
XX(19, ACL, ACL) \
|
||||
/* subversion */ \
|
||||
XX(20, REPORT, REPORT) \
|
||||
XX(21, MKACTIVITY, MKACTIVITY) \
|
||||
XX(22, CHECKOUT, CHECKOUT) \
|
||||
XX(23, MERGE, MERGE) \
|
||||
/* upnp */ \
|
||||
XX(24, MSEARCH, M-SEARCH) \
|
||||
XX(25, NOTIFY, NOTIFY) \
|
||||
XX(26, SUBSCRIBE, SUBSCRIBE) \
|
||||
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
|
||||
/* RFC-5789 */ \
|
||||
XX(28, PATCH, PATCH) \
|
||||
XX(29, PURGE, PURGE) \
|
||||
/* CalDAV */ \
|
||||
XX(30, MKCALENDAR, MKCALENDAR) \
|
||||
/* RFC-2068, section 19.6.1.2 */ \
|
||||
XX(31, LINK, LINK) \
|
||||
XX(32, UNLINK, UNLINK) \
|
||||
/* icecast */ \
|
||||
XX(33, SOURCE, SOURCE) \
|
||||
/* RFC-2326 (RTSP) */ \
|
||||
XX(34, DESCRIBE, DESCRIBE) \
|
||||
XX(35, ANNOUNCE, ANNOUNCE) \
|
||||
XX(36, SETUP, SETUP) \
|
||||
XX(37, PLAY, PLAY) \
|
||||
XX(38, PAUSE, PAUSE) \
|
||||
XX(39, TEARDOWN, TEARDOWN) \
|
||||
XX(40, GET_PARAMETER, GET_PARAMETER) \
|
||||
XX(41, SET_PARAMETER, SET_PARAMETER) \
|
||||
XX(42, REDIRECT, REDIRECT) \
|
||||
XX(43, RECORD, RECORD) \
|
||||
/* RAOP */ \
|
||||
XX(44, FLUSH, FLUSH) \
|
||||
|
||||
enum http_method
|
||||
{
|
||||
#define XX(num, name, string) HTTP_##name = num,
|
||||
HTTP_METHOD_MAP(XX)
|
||||
#undef XX
|
||||
};
|
||||
|
||||
|
||||
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
|
||||
|
||||
|
||||
/* Flag values for http_parser.flags field */
|
||||
enum flags
|
||||
{ F_CHUNKED = 1 << 0
|
||||
, F_CONNECTION_KEEP_ALIVE = 1 << 1
|
||||
, F_CONNECTION_CLOSE = 1 << 2
|
||||
, F_CONNECTION_UPGRADE = 1 << 3
|
||||
, F_TRAILING = 1 << 4
|
||||
, F_UPGRADE = 1 << 5
|
||||
, F_SKIPBODY = 1 << 6
|
||||
, F_CONTENTLENGTH = 1 << 7
|
||||
, F_TRANSFER_ENCODING = 1 << 8
|
||||
};
|
||||
|
||||
|
||||
/* Map for errno-related constants
|
||||
*
|
||||
* The provided argument should be a macro that takes 2 arguments.
|
||||
*/
|
||||
#define HTTP_ERRNO_MAP(XX) \
|
||||
/* No error */ \
|
||||
XX(OK, "success") \
|
||||
\
|
||||
/* Callback-related errors */ \
|
||||
XX(CB_message_begin, "the on_message_begin callback failed") \
|
||||
XX(CB_url, "the on_url callback failed") \
|
||||
XX(CB_header_field, "the on_header_field callback failed") \
|
||||
XX(CB_header_value, "the on_header_value callback failed") \
|
||||
XX(CB_headers_complete, "the on_headers_complete callback failed") \
|
||||
XX(CB_body, "the on_body callback failed") \
|
||||
XX(CB_message_complete, "the on_message_complete callback failed") \
|
||||
XX(CB_status, "the on_status callback failed") \
|
||||
XX(CB_chunk_header, "the on_chunk_header callback failed") \
|
||||
XX(CB_chunk_complete, "the on_chunk_complete callback failed") \
|
||||
\
|
||||
/* Parsing-related errors */ \
|
||||
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
|
||||
XX(HEADER_OVERFLOW, \
|
||||
"too many header bytes seen; overflow detected") \
|
||||
XX(CLOSED_CONNECTION, \
|
||||
"data received after completed connection: close message") \
|
||||
XX(INVALID_VERSION, "invalid HTTP version") \
|
||||
XX(INVALID_STATUS, "invalid HTTP status code") \
|
||||
XX(INVALID_METHOD, "invalid HTTP method") \
|
||||
XX(INVALID_URL, "invalid URL") \
|
||||
XX(INVALID_HOST, "invalid host") \
|
||||
XX(INVALID_PORT, "invalid port") \
|
||||
XX(INVALID_PATH, "invalid path") \
|
||||
XX(INVALID_QUERY_STRING, "invalid query string") \
|
||||
XX(INVALID_FRAGMENT, "invalid fragment") \
|
||||
XX(LF_EXPECTED, "LF character expected") \
|
||||
XX(INVALID_HEADER_TOKEN, "invalid character in header") \
|
||||
XX(INVALID_CONTENT_LENGTH, \
|
||||
"invalid character in content-length header") \
|
||||
XX(UNEXPECTED_CONTENT_LENGTH, \
|
||||
"unexpected content-length header") \
|
||||
XX(INVALID_CHUNK_SIZE, \
|
||||
"invalid character in chunk size header") \
|
||||
XX(INVALID_TRANSFER_ENCODING, \
|
||||
"request has invalid transfer-encoding") \
|
||||
XX(INVALID_CONSTANT, "invalid constant string") \
|
||||
XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
|
||||
XX(STRICT, "strict mode assertion failed") \
|
||||
XX(PAUSED, "parser is paused") \
|
||||
XX(UNKNOWN, "an unknown error occurred")
|
||||
|
||||
|
||||
/* Define HPE_* values for each errno value above */
|
||||
#define HTTP_ERRNO_GEN(n, s) HPE_##n,
|
||||
enum http_errno {
|
||||
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
|
||||
};
|
||||
#undef HTTP_ERRNO_GEN
|
||||
|
||||
|
||||
/* Get an http_errno value from an http_parser */
|
||||
#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
|
||||
|
||||
|
||||
struct http_parser {
|
||||
/** PRIVATE **/
|
||||
unsigned int type : 2; /* enum http_parser_type */
|
||||
unsigned int state : 7; /* enum state from http_parser.c */
|
||||
unsigned int header_state : 7; /* enum header_state from http_parser.c */
|
||||
unsigned int index : 7; /* index into current matcher */
|
||||
unsigned int lenient_http_headers : 1;
|
||||
unsigned int flags : 16; /* F_* values from 'flags' enum; semi-public */
|
||||
|
||||
uint32_t nread; /* # bytes read in various scenarios */
|
||||
uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
|
||||
|
||||
/** READ-ONLY **/
|
||||
unsigned short http_major;
|
||||
unsigned short http_minor;
|
||||
unsigned int status_code : 16; /* responses only */
|
||||
unsigned int method : 8; /* requests only */
|
||||
unsigned int http_errno : 7;
|
||||
|
||||
/* 1 = Upgrade header was present and the parser has exited because of that.
|
||||
* 0 = No upgrade header present.
|
||||
* Should be checked when http_parser_execute() returns in addition to
|
||||
* error checking.
|
||||
*/
|
||||
unsigned int upgrade : 1;
|
||||
|
||||
/** PUBLIC **/
|
||||
void *data; /* A pointer to get hook to the "connection" or "socket" object */
|
||||
};
|
||||
|
||||
|
||||
struct http_parser_settings {
|
||||
http_cb on_message_begin;
|
||||
http_data_cb on_url;
|
||||
http_data_cb on_status;
|
||||
http_data_cb on_header_field;
|
||||
http_data_cb on_header_value;
|
||||
http_cb on_headers_complete;
|
||||
http_data_cb on_body;
|
||||
http_cb on_message_complete;
|
||||
/* When on_chunk_header is called, the current chunk length is stored
|
||||
* in parser->content_length.
|
||||
*/
|
||||
http_cb on_chunk_header;
|
||||
http_cb on_chunk_complete;
|
||||
};
|
||||
|
||||
|
||||
enum http_parser_url_fields
|
||||
{ UF_SCHEMA = 0
|
||||
, UF_HOST = 1
|
||||
, UF_PORT = 2
|
||||
, UF_PATH = 3
|
||||
, UF_QUERY = 4
|
||||
, UF_FRAGMENT = 5
|
||||
, UF_USERINFO = 6
|
||||
, UF_MAX = 7
|
||||
};
|
||||
|
||||
|
||||
/* Result structure for http_parser_parse_url().
|
||||
*
|
||||
* Callers should index into field_data[] with UF_* values iff field_set
|
||||
* has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
|
||||
* because we probably have padding left over), we convert any port to
|
||||
* a uint16_t.
|
||||
*/
|
||||
struct http_parser_url {
|
||||
uint16_t field_set; /* Bitmask of (1 << UF_*) values */
|
||||
uint16_t port; /* Converted UF_PORT string */
|
||||
|
||||
struct {
|
||||
uint16_t off; /* Offset into buffer in which field starts */
|
||||
uint16_t len; /* Length of run in buffer */
|
||||
} field_data[UF_MAX];
|
||||
};
|
||||
|
||||
|
||||
/* Returns the library version. Bits 16-23 contain the major version number,
|
||||
* bits 8-15 the minor version number and bits 0-7 the patch level.
|
||||
* Usage example:
|
||||
*
|
||||
* unsigned long version = http_parser_version();
|
||||
* unsigned major = (version >> 16) & 255;
|
||||
* unsigned minor = (version >> 8) & 255;
|
||||
* unsigned patch = version & 255;
|
||||
* printf("http_parser v%u.%u.%u\n", major, minor, patch);
|
||||
*/
|
||||
unsigned long http_parser_version(void);
|
||||
|
||||
void http_parser_init(http_parser *parser, enum http_parser_type type);
|
||||
|
||||
|
||||
/* Initialize http_parser_settings members to 0
|
||||
*/
|
||||
void http_parser_settings_init(http_parser_settings *settings);
|
||||
|
||||
|
||||
/* Executes the parser. Returns number of parsed bytes. Sets
|
||||
* `parser->http_errno` on error. */
|
||||
size_t http_parser_execute(http_parser *parser,
|
||||
const http_parser_settings *settings,
|
||||
const char *data,
|
||||
size_t len);
|
||||
|
||||
|
||||
/* If http_should_keep_alive() in the on_headers_complete or
|
||||
* on_message_complete callback returns 0, then this should be
|
||||
* the last message on the connection.
|
||||
* If you are the server, respond with the "Connection: close" header.
|
||||
* If you are the client, close the connection.
|
||||
*/
|
||||
int http_should_keep_alive(const http_parser *parser);
|
||||
|
||||
/* Returns a string version of the HTTP method. */
|
||||
const char *http_method_str(enum http_method m);
|
||||
|
||||
/* Returns a string version of the HTTP status code. */
|
||||
const char *http_status_str(enum http_status s);
|
||||
|
||||
/* Return a string name of the given error */
|
||||
const char *http_errno_name(enum http_errno err);
|
||||
|
||||
/* Return a string description of the given error */
|
||||
const char *http_errno_description(enum http_errno err);
|
||||
|
||||
/* Initialize all http_parser_url members to 0 */
|
||||
void http_parser_url_init(struct http_parser_url *u);
|
||||
|
||||
/* Parse a URL; return nonzero on failure */
|
||||
int http_parser_parse_url(const char *buf, size_t buflen,
|
||||
int is_connect,
|
||||
struct http_parser_url *u);
|
||||
|
||||
/* Pause or un-pause the parser; a nonzero value pauses */
|
||||
void http_parser_pause(http_parser *parser, int paused);
|
||||
|
||||
/* Checks if this is the final chunk of the body. */
|
||||
int http_body_is_final(const http_parser *parser);
|
||||
|
||||
/* Change the maximum header size provided at compile time. */
|
||||
void http_parser_set_max_header_size(uint32_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
252
lib/http_request.c
Executable file
252
lib/http_request.c
Executable file
@@ -0,0 +1,252 @@
|
||||
/**
|
||||
* 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "http_request.h"
|
||||
#include "http_parser.h"
|
||||
|
||||
struct http_request_s {
|
||||
http_parser parser;
|
||||
http_parser_settings parser_settings;
|
||||
|
||||
const char *method;
|
||||
char *url;
|
||||
|
||||
char **headers;
|
||||
int headers_size;
|
||||
int headers_index;
|
||||
|
||||
char *data;
|
||||
int datalen;
|
||||
|
||||
int complete;
|
||||
};
|
||||
|
||||
static int
|
||||
on_url(http_parser *parser, const char *at, size_t length)
|
||||
{
|
||||
http_request_t *request = parser->data;
|
||||
int urllen = request->url ? strlen(request->url) : 0;
|
||||
|
||||
request->url = realloc(request->url, urllen+length+1);
|
||||
assert(request->url);
|
||||
|
||||
request->url[urllen] = '\0';
|
||||
strncat(request->url, at, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
on_header_field(http_parser *parser, const char *at, size_t length)
|
||||
{
|
||||
http_request_t *request = parser->data;
|
||||
|
||||
/* Check if our index is a value */
|
||||
if (request->headers_index%2 == 1) {
|
||||
request->headers_index++;
|
||||
}
|
||||
|
||||
/* Allocate space for new field-value pair */
|
||||
if (request->headers_index == request->headers_size) {
|
||||
request->headers_size += 2;
|
||||
request->headers = realloc(request->headers,
|
||||
request->headers_size*sizeof(char*));
|
||||
assert(request->headers);
|
||||
request->headers[request->headers_index] = NULL;
|
||||
request->headers[request->headers_index+1] = NULL;
|
||||
}
|
||||
|
||||
/* Allocate space in the current header string */
|
||||
if (request->headers[request->headers_index] == NULL) {
|
||||
request->headers[request->headers_index] = calloc(1, length+1);
|
||||
} else {
|
||||
request->headers[request->headers_index] = realloc(
|
||||
request->headers[request->headers_index],
|
||||
strlen(request->headers[request->headers_index])+length+1
|
||||
);
|
||||
}
|
||||
assert(request->headers[request->headers_index]);
|
||||
|
||||
strncat(request->headers[request->headers_index], at, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
on_header_value(http_parser *parser, const char *at, size_t length)
|
||||
{
|
||||
http_request_t *request = parser->data;
|
||||
|
||||
/* Check if our index is a field */
|
||||
if (request->headers_index%2 == 0) {
|
||||
request->headers_index++;
|
||||
}
|
||||
|
||||
/* Allocate space in the current header string */
|
||||
if (request->headers[request->headers_index] == NULL) {
|
||||
request->headers[request->headers_index] = calloc(1, length+1);
|
||||
} else {
|
||||
request->headers[request->headers_index] = realloc(
|
||||
request->headers[request->headers_index],
|
||||
strlen(request->headers[request->headers_index])+length+1
|
||||
);
|
||||
}
|
||||
assert(request->headers[request->headers_index]);
|
||||
|
||||
strncat(request->headers[request->headers_index], at, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
on_body(http_parser *parser, const char *at, size_t length)
|
||||
{
|
||||
http_request_t *request = parser->data;
|
||||
|
||||
request->data = realloc(request->data, request->datalen+length);
|
||||
assert(request->data);
|
||||
|
||||
memcpy(request->data+request->datalen, at, length);
|
||||
request->datalen += length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
on_message_complete(http_parser *parser)
|
||||
{
|
||||
http_request_t *request = parser->data;
|
||||
|
||||
request->method = http_method_str(request->parser.method);
|
||||
request->complete = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
http_request_t *
|
||||
http_request_init(void)
|
||||
{
|
||||
http_request_t *request;
|
||||
|
||||
request = calloc(1, sizeof(http_request_t));
|
||||
if (!request) {
|
||||
return NULL;
|
||||
}
|
||||
http_parser_init(&request->parser, HTTP_REQUEST);
|
||||
request->parser.data = request;
|
||||
|
||||
request->parser_settings.on_url = &on_url;
|
||||
request->parser_settings.on_header_field = &on_header_field;
|
||||
request->parser_settings.on_header_value = &on_header_value;
|
||||
request->parser_settings.on_body = &on_body;
|
||||
request->parser_settings.on_message_complete = &on_message_complete;
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
void
|
||||
http_request_destroy(http_request_t *request)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (request) {
|
||||
free(request->url);
|
||||
for (i=0; i<request->headers_size; i++) {
|
||||
free(request->headers[i]);
|
||||
}
|
||||
free(request->headers);
|
||||
free(request->data);
|
||||
free(request);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
http_request_add_data(http_request_t *request, const char *data, int datalen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(request);
|
||||
|
||||
ret = http_parser_execute(&request->parser,
|
||||
&request->parser_settings,
|
||||
data, datalen);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
http_request_is_complete(http_request_t *request)
|
||||
{
|
||||
assert(request);
|
||||
return request->complete;
|
||||
}
|
||||
|
||||
int
|
||||
http_request_has_error(http_request_t *request)
|
||||
{
|
||||
assert(request);
|
||||
return (HTTP_PARSER_ERRNO(&request->parser) != HPE_OK);
|
||||
}
|
||||
|
||||
const char *
|
||||
http_request_get_error_name(http_request_t *request)
|
||||
{
|
||||
assert(request);
|
||||
return http_errno_name(HTTP_PARSER_ERRNO(&request->parser));
|
||||
}
|
||||
|
||||
const char *
|
||||
http_request_get_error_description(http_request_t *request)
|
||||
{
|
||||
assert(request);
|
||||
return http_errno_description(HTTP_PARSER_ERRNO(&request->parser));
|
||||
}
|
||||
|
||||
const char *
|
||||
http_request_get_method(http_request_t *request)
|
||||
{
|
||||
assert(request);
|
||||
return request->method;
|
||||
}
|
||||
|
||||
const char *
|
||||
http_request_get_url(http_request_t *request)
|
||||
{
|
||||
assert(request);
|
||||
return request->url;
|
||||
}
|
||||
|
||||
const char *
|
||||
http_request_get_header(http_request_t *request, const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
assert(request);
|
||||
|
||||
for (i=0; i<request->headers_size; i+=2) {
|
||||
if (!strcmp(request->headers[i], name)) {
|
||||
return request->headers[i+1];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
http_request_get_data(http_request_t *request, int *datalen)
|
||||
{
|
||||
assert(request);
|
||||
|
||||
if (datalen) {
|
||||
*datalen = request->datalen;
|
||||
}
|
||||
return request->data;
|
||||
}
|
||||
36
lib/http_request.h
Executable file
36
lib/http_request.h
Executable file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef HTTP_REQUEST_H
|
||||
#define HTTP_REQUEST_H
|
||||
|
||||
typedef struct http_request_s http_request_t;
|
||||
|
||||
|
||||
http_request_t *http_request_init(void);
|
||||
|
||||
int http_request_add_data(http_request_t *request, const char *data, int datalen);
|
||||
int http_request_is_complete(http_request_t *request);
|
||||
int http_request_has_error(http_request_t *request);
|
||||
|
||||
const char *http_request_get_error_name(http_request_t *request);
|
||||
const char *http_request_get_error_description(http_request_t *request);
|
||||
const char *http_request_get_method(http_request_t *request);
|
||||
const char *http_request_get_url(http_request_t *request);
|
||||
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);
|
||||
|
||||
#endif
|
||||
165
lib/http_response.c
Executable file
165
lib/http_response.c
Executable file
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "http_response.h"
|
||||
#include "compat.h"
|
||||
|
||||
struct http_response_s {
|
||||
int complete;
|
||||
int disconnect;
|
||||
|
||||
char *data;
|
||||
int data_size;
|
||||
int data_length;
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
http_response_add_data(http_response_t *response, const char *data, int datalen)
|
||||
{
|
||||
int newdatasize;
|
||||
|
||||
assert(response);
|
||||
assert(data);
|
||||
assert(datalen > 0);
|
||||
|
||||
newdatasize = response->data_size;
|
||||
while (response->data_size+datalen > newdatasize) {
|
||||
newdatasize *= 2;
|
||||
}
|
||||
if (newdatasize != response->data_size) {
|
||||
response->data = realloc(response->data, newdatasize);
|
||||
assert(response->data);
|
||||
}
|
||||
memcpy(response->data+response->data_length, data, datalen);
|
||||
response->data_length += datalen;
|
||||
}
|
||||
|
||||
http_response_t *
|
||||
http_response_init(const char *protocol, int code, const char *message)
|
||||
{
|
||||
http_response_t *response;
|
||||
char codestr[4];
|
||||
|
||||
assert(code >= 100 && code < 1000);
|
||||
|
||||
/* Convert code into string */
|
||||
memset(codestr, 0, sizeof(codestr));
|
||||
snprintf(codestr, sizeof(codestr), "%u", code);
|
||||
|
||||
response = calloc(1, sizeof(http_response_t));
|
||||
if (!response) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Allocate response data */
|
||||
response->data_size = 1024;
|
||||
response->data = malloc(response->data_size);
|
||||
if (!response->data) {
|
||||
free(response);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Add first line of response to the data array */
|
||||
http_response_add_data(response, protocol, strlen(protocol));
|
||||
http_response_add_data(response, " ", 1);
|
||||
http_response_add_data(response, codestr, strlen(codestr));
|
||||
http_response_add_data(response, " ", 1);
|
||||
http_response_add_data(response, message, strlen(message));
|
||||
http_response_add_data(response, "\r\n", 2);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
void
|
||||
http_response_destroy(http_response_t *response)
|
||||
{
|
||||
if (response) {
|
||||
free(response->data);
|
||||
free(response);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
http_response_add_header(http_response_t *response, const char *name, const char *value)
|
||||
{
|
||||
assert(response);
|
||||
assert(name);
|
||||
assert(value);
|
||||
|
||||
http_response_add_data(response, name, strlen(name));
|
||||
http_response_add_data(response, ": ", 2);
|
||||
http_response_add_data(response, value, strlen(value));
|
||||
http_response_add_data(response, "\r\n", 2);
|
||||
}
|
||||
|
||||
void
|
||||
http_response_finish(http_response_t *response, const char *data, int datalen)
|
||||
{
|
||||
assert(response);
|
||||
assert(datalen==0 || (data && datalen > 0));
|
||||
|
||||
if (data && datalen > 0) {
|
||||
const char *hdrname = "Content-Length";
|
||||
char hdrvalue[16];
|
||||
|
||||
memset(hdrvalue, 0, sizeof(hdrvalue));
|
||||
snprintf(hdrvalue, sizeof(hdrvalue)-1, "%d", datalen);
|
||||
|
||||
/* Add Content-Length header first */
|
||||
http_response_add_data(response, hdrname, strlen(hdrname));
|
||||
http_response_add_data(response, ": ", 2);
|
||||
http_response_add_data(response, hdrvalue, strlen(hdrvalue));
|
||||
http_response_add_data(response, "\r\n\r\n", 4);
|
||||
|
||||
/* Add data to the end of response */
|
||||
http_response_add_data(response, data, datalen);
|
||||
} else {
|
||||
/* Add extra end of line after headers */
|
||||
http_response_add_data(response, "\r\n", 2);
|
||||
}
|
||||
response->complete = 1;
|
||||
}
|
||||
|
||||
void
|
||||
http_response_set_disconnect(http_response_t *response, int disconnect)
|
||||
{
|
||||
assert(response);
|
||||
|
||||
response->disconnect = !!disconnect;
|
||||
}
|
||||
|
||||
int
|
||||
http_response_get_disconnect(http_response_t *response)
|
||||
{
|
||||
assert(response);
|
||||
|
||||
return response->disconnect;
|
||||
}
|
||||
|
||||
const char *
|
||||
http_response_get_data(http_response_t *response, int *datalen)
|
||||
{
|
||||
assert(response);
|
||||
assert(datalen);
|
||||
assert(response->complete);
|
||||
|
||||
*datalen = response->data_length;
|
||||
return response->data;
|
||||
}
|
||||
32
lib/http_response.h
Executable file
32
lib/http_response.h
Executable file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef HTTP_RESPONSE_H
|
||||
#define HTTP_RESPONSE_H
|
||||
|
||||
typedef struct http_response_s http_response_t;
|
||||
|
||||
http_response_t *http_response_init(const char *protocol, int code, const char *message);
|
||||
|
||||
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);
|
||||
|
||||
void http_response_set_disconnect(http_response_t *response, int disconnect);
|
||||
int http_response_get_disconnect(http_response_t *response);
|
||||
|
||||
const char *http_response_get_data(http_response_t *response, int *datalen);
|
||||
|
||||
void http_response_destroy(http_response_t *response);
|
||||
|
||||
#endif
|
||||
458
lib/httpd.c
Executable file
458
lib/httpd.c
Executable file
@@ -0,0 +1,458 @@
|
||||
/**
|
||||
* 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "httpd.h"
|
||||
#include "netutils.h"
|
||||
#include "http_request.h"
|
||||
#include "compat.h"
|
||||
#include "logger.h"
|
||||
|
||||
struct http_connection_s {
|
||||
int connected;
|
||||
|
||||
int socket_fd;
|
||||
void *user_data;
|
||||
http_request_t *request;
|
||||
};
|
||||
typedef struct http_connection_s http_connection_t;
|
||||
|
||||
struct httpd_s {
|
||||
logger_t *logger;
|
||||
httpd_callbacks_t callbacks;
|
||||
|
||||
int max_connections;
|
||||
int open_connections;
|
||||
http_connection_t *connections;
|
||||
|
||||
/* These variables only edited mutex locked */
|
||||
int running;
|
||||
int joined;
|
||||
thread_handle_t thread;
|
||||
mutex_handle_t run_mutex;
|
||||
|
||||
/* Server fds for accepting connections */
|
||||
int server_fd4;
|
||||
int server_fd6;
|
||||
};
|
||||
|
||||
httpd_t *
|
||||
httpd_init(logger_t *logger, httpd_callbacks_t *callbacks, int max_connections)
|
||||
{
|
||||
httpd_t *httpd;
|
||||
|
||||
assert(logger);
|
||||
assert(callbacks);
|
||||
assert(max_connections > 0);
|
||||
|
||||
/* Allocate the httpd_t structure */
|
||||
httpd = calloc(1, sizeof(httpd_t));
|
||||
if (!httpd) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
httpd->max_connections = max_connections;
|
||||
httpd->connections = calloc(max_connections, sizeof(http_connection_t));
|
||||
if (!httpd->connections) {
|
||||
free(httpd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Use the logger provided */
|
||||
httpd->logger = logger;
|
||||
|
||||
/* Save callback pointers */
|
||||
memcpy(&httpd->callbacks, callbacks, sizeof(httpd_callbacks_t));
|
||||
|
||||
/* Initial status joined */
|
||||
httpd->running = 0;
|
||||
httpd->joined = 1;
|
||||
|
||||
return httpd;
|
||||
}
|
||||
|
||||
void
|
||||
httpd_destroy(httpd_t *httpd)
|
||||
{
|
||||
if (httpd) {
|
||||
httpd_stop(httpd);
|
||||
|
||||
free(httpd->connections);
|
||||
free(httpd);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
httpd_add_connection(httpd_t *httpd, int fd, unsigned char *local, int local_len, unsigned char *remote, int remote_len)
|
||||
{
|
||||
void *user_data;
|
||||
int i;
|
||||
|
||||
for (i=0; i<httpd->max_connections; i++) {
|
||||
if (!httpd->connections[i].connected) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == httpd->max_connections) {
|
||||
/* This code should never be reached, we do not select server_fds when full */
|
||||
logger_log(httpd->logger, LOGGER_INFO, "Max connections reached");
|
||||
return -1;
|
||||
}
|
||||
|
||||
user_data = httpd->callbacks.conn_init(httpd->callbacks.opaque, local, local_len, remote, remote_len);
|
||||
if (!user_data) {
|
||||
logger_log(httpd->logger, LOGGER_ERR, "Error initializing HTTP request handler");
|
||||
return -1;
|
||||
}
|
||||
|
||||
httpd->open_connections++;
|
||||
httpd->connections[i].socket_fd = fd;
|
||||
httpd->connections[i].connected = 1;
|
||||
httpd->connections[i].user_data = user_data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
httpd_accept_connection(httpd_t *httpd, int server_fd, int is_ipv6)
|
||||
{
|
||||
struct sockaddr_storage remote_saddr;
|
||||
socklen_t remote_saddrlen;
|
||||
struct sockaddr_storage local_saddr;
|
||||
socklen_t local_saddrlen;
|
||||
unsigned char *local, *remote;
|
||||
int local_len, remote_len;
|
||||
int ret, fd;
|
||||
|
||||
remote_saddrlen = sizeof(remote_saddr);
|
||||
fd = accept(server_fd, (struct sockaddr *)&remote_saddr, &remote_saddrlen);
|
||||
if (fd == -1) {
|
||||
/* FIXME: Error happened */
|
||||
return -1;
|
||||
}
|
||||
|
||||
local_saddrlen = sizeof(local_saddr);
|
||||
ret = getsockname(fd, (struct sockaddr *)&local_saddr, &local_saddrlen);
|
||||
if (ret == -1) {
|
||||
shutdown(fd, SHUT_RDWR);
|
||||
closesocket(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
logger_log(httpd->logger, LOGGER_INFO, "Accepted %s client on socket %d",
|
||||
(is_ipv6 ? "IPv6" : "IPv4"), fd);
|
||||
local = netutils_get_address(&local_saddr, &local_len);
|
||||
remote = netutils_get_address(&remote_saddr, &remote_len);
|
||||
|
||||
ret = httpd_add_connection(httpd, fd, local, local_len, remote, remote_len);
|
||||
if (ret == -1) {
|
||||
shutdown(fd, SHUT_RDWR);
|
||||
closesocket(fd);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
httpd_remove_connection(httpd_t *httpd, http_connection_t *connection)
|
||||
{
|
||||
if (connection->request) {
|
||||
http_request_destroy(connection->request);
|
||||
connection->request = NULL;
|
||||
}
|
||||
httpd->callbacks.conn_destroy(connection->user_data);
|
||||
shutdown(connection->socket_fd, SHUT_WR);
|
||||
closesocket(connection->socket_fd);
|
||||
connection->connected = 0;
|
||||
httpd->open_connections--;
|
||||
}
|
||||
|
||||
static THREAD_RETVAL
|
||||
httpd_thread(void *arg)
|
||||
{
|
||||
httpd_t *httpd = arg;
|
||||
char buffer[1024];
|
||||
int i;
|
||||
|
||||
assert(httpd);
|
||||
|
||||
while (1) {
|
||||
fd_set rfds;
|
||||
struct timeval tv;
|
||||
int nfds=0;
|
||||
int ret;
|
||||
|
||||
MUTEX_LOCK(httpd->run_mutex);
|
||||
if (!httpd->running) {
|
||||
MUTEX_UNLOCK(httpd->run_mutex);
|
||||
break;
|
||||
}
|
||||
MUTEX_UNLOCK(httpd->run_mutex);
|
||||
|
||||
/* Set timeout value to 5ms */
|
||||
tv.tv_sec = 1;
|
||||
tv.tv_usec = 5000;
|
||||
|
||||
/* Get the correct nfds value and set rfds */
|
||||
FD_ZERO(&rfds);
|
||||
if (httpd->open_connections < httpd->max_connections) {
|
||||
if (httpd->server_fd4 != -1) {
|
||||
FD_SET(httpd->server_fd4, &rfds);
|
||||
if (nfds <= httpd->server_fd4) {
|
||||
nfds = httpd->server_fd4+1;
|
||||
}
|
||||
}
|
||||
if (httpd->server_fd6 != -1) {
|
||||
FD_SET(httpd->server_fd6, &rfds);
|
||||
if (nfds <= httpd->server_fd6) {
|
||||
nfds = httpd->server_fd6+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i=0; i<httpd->max_connections; i++) {
|
||||
int socket_fd;
|
||||
if (!httpd->connections[i].connected) {
|
||||
continue;
|
||||
}
|
||||
socket_fd = httpd->connections[i].socket_fd;
|
||||
FD_SET(socket_fd, &rfds);
|
||||
if (nfds <= socket_fd) {
|
||||
nfds = socket_fd+1;
|
||||
}
|
||||
}
|
||||
|
||||
ret = select(nfds, &rfds, NULL, NULL, &tv);
|
||||
if (ret == 0) {
|
||||
/* Timeout happened */
|
||||
continue;
|
||||
} else if (ret == -1) {
|
||||
logger_log(httpd->logger, LOGGER_ERR, "httpd error in select");
|
||||
break;
|
||||
}
|
||||
|
||||
if (httpd->open_connections < httpd->max_connections &&
|
||||
httpd->server_fd4 != -1 && FD_ISSET(httpd->server_fd4, &rfds)) {
|
||||
ret = httpd_accept_connection(httpd, httpd->server_fd4, 0);
|
||||
if (ret == -1) {
|
||||
logger_log(httpd->logger, LOGGER_ERR, "httpd error in accept ipv4");
|
||||
break;
|
||||
} else if (ret == 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (httpd->open_connections < httpd->max_connections &&
|
||||
httpd->server_fd6 != -1 && FD_ISSET(httpd->server_fd6, &rfds)) {
|
||||
ret = httpd_accept_connection(httpd, httpd->server_fd6, 1);
|
||||
if (ret == -1) {
|
||||
logger_log(httpd->logger, LOGGER_ERR, "httpd error in accept ipv6");
|
||||
break;
|
||||
} else if (ret == 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
for (i=0; i<httpd->max_connections; i++) {
|
||||
http_connection_t *connection = &httpd->connections[i];
|
||||
|
||||
if (!connection->connected) {
|
||||
continue;
|
||||
}
|
||||
if (!FD_ISSET(connection->socket_fd, &rfds)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If not in the middle of request, allocate one */
|
||||
if (!connection->request) {
|
||||
connection->request = http_request_init();
|
||||
assert(connection->request);
|
||||
}
|
||||
|
||||
logger_log(httpd->logger, LOGGER_DEBUG, "httpd receiving on socket %d", connection->socket_fd);
|
||||
ret = recv(connection->socket_fd, buffer, sizeof(buffer), 0);
|
||||
if (ret == 0) {
|
||||
logger_log(httpd->logger, LOGGER_INFO, "Connection closed for socket %d", connection->socket_fd);
|
||||
httpd_remove_connection(httpd, connection);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Parse HTTP request from data read from connection */
|
||||
http_request_add_data(connection->request, buffer, ret);
|
||||
if (http_request_has_error(connection->request)) {
|
||||
logger_log(httpd->logger, LOGGER_ERR, "httpd error in parsing: %s", http_request_get_error_name(connection->request));
|
||||
httpd_remove_connection(httpd, connection);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If request is finished, process and deallocate */
|
||||
if (http_request_is_complete(connection->request)) {
|
||||
http_response_t *response = NULL;
|
||||
// Callback the received data to raop
|
||||
httpd->callbacks.conn_request(connection->user_data, connection->request, &response);
|
||||
http_request_destroy(connection->request);
|
||||
connection->request = NULL;
|
||||
|
||||
if (response) {
|
||||
const char *data;
|
||||
int datalen;
|
||||
int written;
|
||||
int ret;
|
||||
|
||||
/* Get response data and datalen */
|
||||
data = http_response_get_data(response, &datalen);
|
||||
|
||||
written = 0;
|
||||
while (written < datalen) {
|
||||
ret = send(connection->socket_fd, data+written, datalen-written, 0);
|
||||
if (ret == -1) {
|
||||
logger_log(httpd->logger, LOGGER_ERR, "httpd error in sending data");
|
||||
break;
|
||||
}
|
||||
written += ret;
|
||||
}
|
||||
|
||||
if (http_response_get_disconnect(response)) {
|
||||
logger_log(httpd->logger, LOGGER_INFO, "Disconnecting on software request");
|
||||
httpd_remove_connection(httpd, connection);
|
||||
}
|
||||
} else {
|
||||
logger_log(httpd->logger, LOGGER_WARNING, "httpd didn't get response");
|
||||
}
|
||||
http_response_destroy(response);
|
||||
} else {
|
||||
logger_log(httpd->logger, LOGGER_DEBUG, "Request not complete, waiting for more data...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove all connections that are still connected */
|
||||
for (i=0; i<httpd->max_connections; i++) {
|
||||
http_connection_t *connection = &httpd->connections[i];
|
||||
|
||||
if (!connection->connected) {
|
||||
continue;
|
||||
}
|
||||
logger_log(httpd->logger, LOGGER_INFO, "Removing connection for socket %d", connection->socket_fd);
|
||||
httpd_remove_connection(httpd, connection);
|
||||
}
|
||||
|
||||
/* Close server sockets since they are not used any more */
|
||||
if (httpd->server_fd4 != -1) {
|
||||
shutdown(httpd->server_fd4, SHUT_RDWR);
|
||||
closesocket(httpd->server_fd4);
|
||||
httpd->server_fd4 = -1;
|
||||
}
|
||||
if (httpd->server_fd6 != -1) {
|
||||
shutdown(httpd->server_fd6, SHUT_RDWR);
|
||||
closesocket(httpd->server_fd6);
|
||||
httpd->server_fd6 = -1;
|
||||
}
|
||||
|
||||
// Ensure running reflects the actual state
|
||||
MUTEX_LOCK(httpd->run_mutex);
|
||||
httpd->running = 0;
|
||||
MUTEX_UNLOCK(httpd->run_mutex);
|
||||
|
||||
logger_log(httpd->logger, LOGGER_DEBUG, "Exiting HTTP thread");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
httpd_start(httpd_t *httpd, unsigned short *port)
|
||||
{
|
||||
/* How many connection attempts are kept in queue */
|
||||
int backlog = 5;
|
||||
|
||||
assert(httpd);
|
||||
assert(port);
|
||||
|
||||
MUTEX_LOCK(httpd->run_mutex);
|
||||
if (httpd->running || !httpd->joined) {
|
||||
MUTEX_UNLOCK(httpd->run_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
httpd->server_fd4 = netutils_init_socket(port, 0, 0);
|
||||
if (httpd->server_fd4 == -1) {
|
||||
logger_log(httpd->logger, LOGGER_ERR, "Error initialising socket %d", SOCKET_GET_ERROR());
|
||||
MUTEX_UNLOCK(httpd->run_mutex);
|
||||
return -1;
|
||||
}
|
||||
httpd->server_fd6 = -1;/*= 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");
|
||||
}*/
|
||||
|
||||
if (httpd->server_fd4 != -1 && listen(httpd->server_fd4, backlog) == -1) {
|
||||
logger_log(httpd->logger, LOGGER_ERR, "Error listening to IPv4 socket");
|
||||
closesocket(httpd->server_fd4);
|
||||
closesocket(httpd->server_fd6);
|
||||
MUTEX_UNLOCK(httpd->run_mutex);
|
||||
return -2;
|
||||
}
|
||||
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);
|
||||
closesocket(httpd->server_fd6);
|
||||
MUTEX_UNLOCK(httpd->run_mutex);
|
||||
return -2;
|
||||
}
|
||||
logger_log(httpd->logger, LOGGER_INFO, "Initialized server socket(s)");
|
||||
|
||||
/* Set values correctly and create new thread */
|
||||
httpd->running = 1;
|
||||
httpd->joined = 0;
|
||||
THREAD_CREATE(httpd->thread, httpd_thread, httpd);
|
||||
MUTEX_UNLOCK(httpd->run_mutex);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
httpd_is_running(httpd_t *httpd)
|
||||
{
|
||||
int running;
|
||||
|
||||
assert(httpd);
|
||||
|
||||
MUTEX_LOCK(httpd->run_mutex);
|
||||
running = httpd->running || !httpd->joined;
|
||||
MUTEX_UNLOCK(httpd->run_mutex);
|
||||
|
||||
return running;
|
||||
}
|
||||
|
||||
void
|
||||
httpd_stop(httpd_t *httpd)
|
||||
{
|
||||
assert(httpd);
|
||||
|
||||
MUTEX_LOCK(httpd->run_mutex);
|
||||
if (!httpd->running || httpd->joined) {
|
||||
MUTEX_UNLOCK(httpd->run_mutex);
|
||||
return;
|
||||
}
|
||||
httpd->running = 0;
|
||||
MUTEX_UNLOCK(httpd->run_mutex);
|
||||
|
||||
THREAD_JOIN(httpd->thread);
|
||||
|
||||
MUTEX_LOCK(httpd->run_mutex);
|
||||
httpd->joined = 1;
|
||||
MUTEX_UNLOCK(httpd->run_mutex);
|
||||
}
|
||||
|
||||
43
lib/httpd.h
Executable file
43
lib/httpd.h
Executable file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef HTTPD_H
|
||||
#define HTTPD_H
|
||||
|
||||
#include "logger.h"
|
||||
#include "http_request.h"
|
||||
#include "http_response.h"
|
||||
|
||||
typedef struct httpd_s httpd_t;
|
||||
|
||||
struct httpd_callbacks_s {
|
||||
void* opaque;
|
||||
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);
|
||||
};
|
||||
typedef struct httpd_callbacks_s httpd_callbacks_t;
|
||||
|
||||
|
||||
httpd_t *httpd_init(logger_t *logger, httpd_callbacks_t *callbacks, int max_connections);
|
||||
|
||||
int httpd_is_running(httpd_t *httpd);
|
||||
|
||||
int httpd_start(httpd_t *httpd, unsigned short *port);
|
||||
void httpd_stop(httpd_t *httpd);
|
||||
|
||||
void httpd_destroy(httpd_t *httpd);
|
||||
|
||||
|
||||
#endif
|
||||
139
lib/logger.c
Executable file
139
lib/logger.c
Executable file
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "logger.h"
|
||||
#include "compat.h"
|
||||
|
||||
struct logger_s {
|
||||
mutex_handle_t lvl_mutex;
|
||||
mutex_handle_t cb_mutex;
|
||||
|
||||
int level;
|
||||
void *cls;
|
||||
logger_callback_t callback;
|
||||
};
|
||||
|
||||
logger_t *
|
||||
logger_init()
|
||||
{
|
||||
logger_t *logger = calloc(1, sizeof(logger_t));
|
||||
assert(logger);
|
||||
|
||||
MUTEX_CREATE(logger->lvl_mutex);
|
||||
MUTEX_CREATE(logger->cb_mutex);
|
||||
|
||||
logger->level = LOGGER_WARNING;
|
||||
logger->callback = NULL;
|
||||
return logger;
|
||||
}
|
||||
|
||||
void
|
||||
logger_destroy(logger_t *logger)
|
||||
{
|
||||
MUTEX_DESTROY(logger->lvl_mutex);
|
||||
MUTEX_DESTROY(logger->cb_mutex);
|
||||
free(logger);
|
||||
}
|
||||
|
||||
void
|
||||
logger_set_level(logger_t *logger, int level)
|
||||
{
|
||||
assert(logger);
|
||||
|
||||
MUTEX_LOCK(logger->lvl_mutex);
|
||||
logger->level = level;
|
||||
MUTEX_UNLOCK(logger->lvl_mutex);
|
||||
}
|
||||
|
||||
void
|
||||
logger_set_callback(logger_t *logger, logger_callback_t callback, void *cls)
|
||||
{
|
||||
assert(logger);
|
||||
|
||||
MUTEX_LOCK(logger->cb_mutex);
|
||||
logger->cls = cls;
|
||||
logger->callback = callback;
|
||||
MUTEX_UNLOCK(logger->cb_mutex);
|
||||
}
|
||||
|
||||
static char *
|
||||
logger_utf8_to_local(const char *str)
|
||||
{
|
||||
char *ret = NULL;
|
||||
|
||||
/* FIXME: This is only implemented on Windows for now */
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
int wclen, mblen;
|
||||
WCHAR *wcstr;
|
||||
BOOL failed;
|
||||
|
||||
wclen = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
|
||||
wcstr = malloc(sizeof(WCHAR) * wclen);
|
||||
MultiByteToWideChar(CP_UTF8, 0, str, -1, wcstr, wclen);
|
||||
|
||||
mblen = WideCharToMultiByte(CP_ACP, 0, wcstr, wclen, NULL, 0, NULL, &failed);
|
||||
if (failed) {
|
||||
/* Invalid characters in input, conversion failed */
|
||||
free(wcstr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = malloc(sizeof(CHAR) * mblen);
|
||||
WideCharToMultiByte(CP_ACP, 0, wcstr, wclen, ret, mblen, NULL, NULL);
|
||||
free(wcstr);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
logger_log(logger_t *logger, int level, const char *fmt, ...)
|
||||
{
|
||||
char buffer[4096];
|
||||
va_list ap;
|
||||
|
||||
MUTEX_LOCK(logger->lvl_mutex);
|
||||
if (level > logger->level) {
|
||||
MUTEX_UNLOCK(logger->lvl_mutex);
|
||||
return;
|
||||
}
|
||||
MUTEX_UNLOCK(logger->lvl_mutex);
|
||||
|
||||
buffer[sizeof(buffer)-1] = '\0';
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buffer, sizeof(buffer)-1, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
MUTEX_LOCK(logger->cb_mutex);
|
||||
if (logger->callback) {
|
||||
logger->callback(logger->cls, level, buffer);
|
||||
MUTEX_UNLOCK(logger->cb_mutex);
|
||||
} else {
|
||||
char *local;
|
||||
MUTEX_UNLOCK(logger->cb_mutex);
|
||||
local = logger_utf8_to_local(buffer);
|
||||
if (local) {
|
||||
fprintf(stderr, "%s\n", local);
|
||||
free(local);
|
||||
} else {
|
||||
fprintf(stderr, "%s\n", buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
48
lib/logger.h
Executable file
48
lib/logger.h
Executable file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef LOGGER_H
|
||||
#define LOGGER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Define syslog style log levels */
|
||||
#define LOGGER_EMERG 0 /* system is unusable */
|
||||
#define LOGGER_ALERT 1 /* action must be taken immediately */
|
||||
#define LOGGER_CRIT 2 /* critical conditions */
|
||||
#define LOGGER_ERR 3 /* error conditions */
|
||||
#define LOGGER_WARNING 4 /* warning conditions */
|
||||
#define LOGGER_NOTICE 5 /* normal but significant condition */
|
||||
#define LOGGER_INFO 6 /* informational */
|
||||
#define LOGGER_DEBUG 7 /* debug-level messages */
|
||||
|
||||
typedef void (*logger_callback_t)(void *cls, int level, const char *msg);
|
||||
|
||||
typedef struct logger_s logger_t;
|
||||
|
||||
logger_t *logger_init();
|
||||
void logger_destroy(logger_t *logger);
|
||||
|
||||
void logger_set_level(logger_t *logger, int level);
|
||||
void logger_set_callback(logger_t *logger, logger_callback_t callback, void *cls);
|
||||
|
||||
void logger_log(logger_t *logger, int level, const char *fmt, ...);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
53
lib/memalign.h
Executable file
53
lib/memalign.h
Executable file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef MEMALIGN_H
|
||||
#define MEMALIGN_H
|
||||
|
||||
#if defined(WIN32)
|
||||
|
||||
#define SYSTEM_GET_PAGESIZE(ret) do {\
|
||||
SYSTEM_INFO si;\
|
||||
GetSystemInfo(&si);\
|
||||
ret = si.dwPageSize;\
|
||||
} while(0)
|
||||
#define SYSTEM_GET_TIME(ret) ret = timeGetTime()
|
||||
|
||||
#define ALIGNED_MALLOC(memptr, alignment, size) do {\
|
||||
char *ptr = malloc(sizeof(void*) + (size) + (alignment)-1);\
|
||||
memptr = NULL;\
|
||||
if (ptr) {\
|
||||
size_t ptrval = (size_t)ptr + sizeof(void*) + (alignment)-1;\
|
||||
ptrval = ptrval / (alignment) * (alignment);\
|
||||
memptr = (void *)ptrval;\
|
||||
*(((void **)memptr)-1) = ptr;\
|
||||
}\
|
||||
} while(0)
|
||||
#define ALIGNED_FREE(memptr) free(*(((void **)memptr)-1))
|
||||
|
||||
#else
|
||||
|
||||
#define SYSTEM_GET_PAGESIZE(ret) ret = sysconf(_SC_PAGESIZE)
|
||||
#define SYSTEM_GET_TIME(ret) do {\
|
||||
struct timeval tv;\
|
||||
gettimeofday(&tv, NULL);\
|
||||
ret = (unsigned int)(tv.tv_sec*1000 + tv.tv_usec/1000);\
|
||||
} while(0)
|
||||
|
||||
#define ALIGNED_MALLOC(memptr, alignment, size) if (posix_memalign((void **)&memptr, alignment, size)) memptr = NULL
|
||||
#define ALIGNED_FREE(memptr) free(memptr)
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
142
lib/mirror_buffer.c
Executable file
142
lib/mirror_buffer.c
Executable file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (c) 2019 dsafa22, All Rights Reserved.
|
||||
*
|
||||
* 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 "mirror_buffer.h"
|
||||
#include "raop_rtp.h"
|
||||
#include "raop_rtp.h"
|
||||
#include <stdint.h>
|
||||
#include "crypto.h"
|
||||
#include "compat.h"
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
//#define DUMP_KEI_IV
|
||||
struct mirror_buffer_s {
|
||||
logger_t *logger;
|
||||
aes_ctx_t *aes_ctx;
|
||||
int nextDecryptCount;
|
||||
uint8_t og[16];
|
||||
/* AES key and IV */
|
||||
// Need secondary processing to use
|
||||
unsigned char aeskey[RAOP_AESKEY_LEN];
|
||||
unsigned char ecdh_secret[32];
|
||||
};
|
||||
|
||||
void
|
||||
mirror_buffer_init_aes(mirror_buffer_t *mirror_buffer, uint64_t streamConnectionID)
|
||||
{
|
||||
sha_ctx_t *ctx = sha_init();
|
||||
unsigned char eaeskey[64] = {};
|
||||
memcpy(eaeskey, mirror_buffer->aeskey, 16);
|
||||
sha_update(ctx, eaeskey, 16);
|
||||
sha_update(ctx, mirror_buffer->ecdh_secret, 32);
|
||||
sha_final(ctx, eaeskey, NULL);
|
||||
|
||||
unsigned char hash1[64];
|
||||
unsigned char hash2[64];
|
||||
char* skey = "AirPlayStreamKey";
|
||||
char* siv = "AirPlayStreamIV";
|
||||
unsigned char skeyall[255];
|
||||
unsigned char sivall[255];
|
||||
sprintf((char*) skeyall, "%s%llu", skey, streamConnectionID);
|
||||
sprintf((char*) sivall, "%s%llu", siv, streamConnectionID);
|
||||
sha_reset(ctx);
|
||||
sha_update(ctx, skeyall, strlen((char*) skeyall));
|
||||
sha_update(ctx, eaeskey, 16);
|
||||
sha_final(ctx, hash1, NULL);
|
||||
|
||||
sha_reset(ctx);
|
||||
sha_update(ctx, sivall, strlen((char*) sivall));
|
||||
sha_update(ctx, eaeskey, 16);
|
||||
sha_final(ctx, hash2, NULL);
|
||||
sha_destroy(ctx);
|
||||
|
||||
unsigned char decrypt_aeskey[16];
|
||||
unsigned char decrypt_aesiv[16];
|
||||
memcpy(decrypt_aeskey, hash1, 16);
|
||||
memcpy(decrypt_aesiv, hash2, 16);
|
||||
#ifdef DUMP_KEI_IV
|
||||
FILE* keyfile = fopen("/sdcard/111.keyiv", "wb");
|
||||
fwrite(decrypt_aeskey, 16, 1, keyfile);
|
||||
fwrite(decrypt_aesiv, 16, 1, keyfile);
|
||||
fclose(keyfile);
|
||||
#endif
|
||||
// Need to be initialized externally
|
||||
mirror_buffer->aes_ctx = aes_ctr_init(decrypt_aeskey, decrypt_aesiv);
|
||||
mirror_buffer->nextDecryptCount = 0;
|
||||
}
|
||||
|
||||
mirror_buffer_t *
|
||||
mirror_buffer_init(logger_t *logger,
|
||||
const unsigned char *aeskey,
|
||||
const unsigned char *ecdh_secret)
|
||||
{
|
||||
mirror_buffer_t *mirror_buffer;
|
||||
assert(aeskey);
|
||||
assert(ecdh_secret);
|
||||
mirror_buffer = calloc(1, sizeof(mirror_buffer_t));
|
||||
if (!mirror_buffer) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy(mirror_buffer->aeskey, aeskey, RAOP_AESKEY_LEN);
|
||||
memcpy(mirror_buffer->ecdh_secret, ecdh_secret, 32);
|
||||
mirror_buffer->logger = logger;
|
||||
mirror_buffer->nextDecryptCount = 0;
|
||||
//mirror_buffer_init_aes(mirror_buffer, aeskey, ecdh_secret, streamConnectionID);
|
||||
return mirror_buffer;
|
||||
}
|
||||
|
||||
void mirror_buffer_decrypt(mirror_buffer_t *mirror_buffer, unsigned char* input, unsigned char* output, int inputLen) {
|
||||
// Start decrypting
|
||||
if (mirror_buffer->nextDecryptCount > 0) {//mirror_buffer->nextDecryptCount = 10
|
||||
for (int i = 0; i < mirror_buffer->nextDecryptCount; i++) {
|
||||
output[i] = (input[i] ^ mirror_buffer->og[(16 - mirror_buffer->nextDecryptCount) + i]);
|
||||
}
|
||||
}
|
||||
// Handling encrypted bytes
|
||||
int encryptlen = ((inputLen - mirror_buffer->nextDecryptCount) / 16) * 16;
|
||||
// Aes decryption
|
||||
aes_ctr_start_fresh_block(mirror_buffer->aes_ctx);
|
||||
aes_ctr_decrypt(mirror_buffer->aes_ctx, input + mirror_buffer->nextDecryptCount,
|
||||
input + mirror_buffer->nextDecryptCount, encryptlen);
|
||||
// Copy to output
|
||||
memcpy(output + mirror_buffer->nextDecryptCount, input + mirror_buffer->nextDecryptCount, encryptlen);
|
||||
int outputlength = mirror_buffer->nextDecryptCount + encryptlen;
|
||||
// Processing remaining length
|
||||
int restlen = (inputLen - mirror_buffer->nextDecryptCount) % 16;
|
||||
int reststart = inputLen - restlen;
|
||||
mirror_buffer->nextDecryptCount = 0;
|
||||
if (restlen > 0) {
|
||||
memset(mirror_buffer->og, 0, 16);
|
||||
memcpy(mirror_buffer->og, input + reststart, restlen);
|
||||
aes_ctr_decrypt(mirror_buffer->aes_ctx, mirror_buffer->og, mirror_buffer->og, 16);
|
||||
for (int j = 0; j < restlen; j++) {
|
||||
output[reststart + j] = mirror_buffer->og[j];
|
||||
}
|
||||
outputlength += restlen;
|
||||
mirror_buffer->nextDecryptCount = 16 - restlen;// Difference 16-6=10 bytes
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mirror_buffer_destroy(mirror_buffer_t *mirror_buffer)
|
||||
{
|
||||
if (mirror_buffer) {
|
||||
aes_ctr_destroy(mirror_buffer->aes_ctx);
|
||||
free(mirror_buffer);
|
||||
}
|
||||
}
|
||||
30
lib/mirror_buffer.h
Executable file
30
lib/mirror_buffer.h
Executable file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2019 dsafa22, All Rights Reserved.
|
||||
*
|
||||
* 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 MIRROR_BUFFER_H
|
||||
#define MIRROR_BUFFER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "logger.h"
|
||||
|
||||
typedef struct mirror_buffer_s mirror_buffer_t;
|
||||
|
||||
|
||||
mirror_buffer_t *mirror_buffer_init( logger_t *logger,
|
||||
const unsigned char *aeskey,
|
||||
const unsigned char *ecdh_secret);
|
||||
void mirror_buffer_init_aes(mirror_buffer_t *mirror_buffer, uint64_t streamConnectionID);
|
||||
void mirror_buffer_decrypt(mirror_buffer_t *raop_mirror, unsigned char* input, unsigned char* output, int datalen);
|
||||
void mirror_buffer_destroy(mirror_buffer_t *mirror_buffer);
|
||||
#endif //MIRROR_BUFFER_H
|
||||
203
lib/netutils.c
Executable file
203
lib/netutils.c
Executable file
@@ -0,0 +1,203 @@
|
||||
/**
|
||||
* 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
int
|
||||
netutils_init()
|
||||
{
|
||||
#ifdef WIN32
|
||||
WORD wVersionRequested;
|
||||
WSADATA wsaData;
|
||||
int ret;
|
||||
|
||||
wVersionRequested = MAKEWORD(2, 2);
|
||||
ret = WSAStartup(wVersionRequested, &wsaData);
|
||||
if (ret) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (LOBYTE(wsaData.wVersion) != 2 ||
|
||||
HIBYTE(wsaData.wVersion) != 2) {
|
||||
/* Version mismatch, requested version not found */
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
netutils_cleanup()
|
||||
{
|
||||
#ifdef WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned char *
|
||||
netutils_get_address(void *sockaddr, int *length)
|
||||
{
|
||||
unsigned char ipv4_prefix[] = { 0,0,0,0,0,0,0,0,0,0,255,255 };
|
||||
struct sockaddr *address = sockaddr;
|
||||
|
||||
assert(address);
|
||||
assert(length);
|
||||
|
||||
if (address->sa_family == AF_INET) {
|
||||
struct sockaddr_in *sin;
|
||||
|
||||
sin = (struct sockaddr_in *)address;
|
||||
*length = sizeof(sin->sin_addr.s_addr);
|
||||
return (unsigned char *)&sin->sin_addr.s_addr;
|
||||
} else if (address->sa_family == AF_INET6) {
|
||||
struct sockaddr_in6 *sin6;
|
||||
|
||||
sin6 = (struct sockaddr_in6 *)address;
|
||||
if (!memcmp(sin6->sin6_addr.s6_addr, ipv4_prefix, 12)) {
|
||||
/* Actually an embedded IPv4 address */
|
||||
*length = sizeof(sin6->sin6_addr.s6_addr)-12;
|
||||
return (sin6->sin6_addr.s6_addr+12);
|
||||
}
|
||||
*length = sizeof(sin6->sin6_addr.s6_addr);
|
||||
return sin6->sin6_addr.s6_addr;
|
||||
}
|
||||
|
||||
*length = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
netutils_init_socket(unsigned short *port, int use_ipv6, int use_udp)
|
||||
{
|
||||
int family = use_ipv6 ? AF_INET6 : AF_INET;
|
||||
int type = use_udp ? SOCK_DGRAM : SOCK_STREAM;
|
||||
int proto = use_udp ? IPPROTO_UDP : IPPROTO_TCP;
|
||||
|
||||
struct sockaddr_storage saddr;
|
||||
socklen_t socklen;
|
||||
int server_fd;
|
||||
int ret;
|
||||
int reuseaddr = 1;
|
||||
|
||||
assert(port);
|
||||
|
||||
server_fd = socket(family, type, proto);
|
||||
if (server_fd == -1) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof (reuseaddr));
|
||||
if (ret == -1) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
memset(&saddr, 0, sizeof(saddr));
|
||||
if (use_ipv6) {
|
||||
struct sockaddr_in6 *sin6ptr = (struct sockaddr_in6 *)&saddr;
|
||||
int v6only = 1;
|
||||
|
||||
/* Initialize sockaddr for bind */
|
||||
sin6ptr->sin6_family = family;
|
||||
sin6ptr->sin6_addr = in6addr_any;
|
||||
sin6ptr->sin6_port = htons(*port);
|
||||
|
||||
#ifndef WIN32
|
||||
/* Make sure we only listen to IPv6 addresses */
|
||||
setsockopt(server_fd, IPPROTO_IPV6, IPV6_V6ONLY,
|
||||
(char *) &v6only, sizeof(v6only));
|
||||
#endif
|
||||
|
||||
socklen = sizeof(*sin6ptr);
|
||||
ret = bind(server_fd, (struct sockaddr *)sin6ptr, socklen);
|
||||
if (ret == -1) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = getsockname(server_fd, (struct sockaddr *)sin6ptr, &socklen);
|
||||
if (ret == -1) {
|
||||
goto cleanup;
|
||||
}
|
||||
*port = ntohs(sin6ptr->sin6_port);
|
||||
} else {
|
||||
struct sockaddr_in *sinptr = (struct sockaddr_in *)&saddr;
|
||||
|
||||
/* Initialize sockaddr for bind */
|
||||
sinptr->sin_family = family;
|
||||
sinptr->sin_addr.s_addr = INADDR_ANY;
|
||||
sinptr->sin_port = htons(*port);
|
||||
|
||||
socklen = sizeof(*sinptr);
|
||||
ret = bind(server_fd, (struct sockaddr *)sinptr, socklen);
|
||||
if (ret == -1) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = getsockname(server_fd, (struct sockaddr *)sinptr, &socklen);
|
||||
if (ret == -1) {
|
||||
goto cleanup;
|
||||
}
|
||||
*port = ntohs(sinptr->sin_port);
|
||||
}
|
||||
return server_fd;
|
||||
|
||||
cleanup:
|
||||
ret = SOCKET_GET_ERROR();
|
||||
if (server_fd != -1) {
|
||||
closesocket(server_fd);
|
||||
}
|
||||
SOCKET_SET_ERROR(ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Src is the ip address
|
||||
int
|
||||
netutils_parse_address(int family, const char *src, void *dst, int dstlen)
|
||||
{
|
||||
struct addrinfo *result;
|
||||
struct addrinfo *ptr;
|
||||
struct addrinfo hints;
|
||||
int length;
|
||||
int ret;
|
||||
|
||||
if (family != AF_INET && family != AF_INET6) {
|
||||
return -1;
|
||||
}
|
||||
if (!src || !dst) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = family;
|
||||
hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
|
||||
|
||||
ret = getaddrinfo(src, NULL, &hints, &result);
|
||||
if (ret != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
length = -1;
|
||||
for (ptr=result; ptr!=NULL; ptr=ptr->ai_next) {
|
||||
if (family == ptr->ai_family && (unsigned int)dstlen >= ptr->ai_addrlen) {
|
||||
memcpy(dst, ptr->ai_addr, ptr->ai_addrlen);
|
||||
length = ptr->ai_addrlen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
freeaddrinfo(result);
|
||||
return length;
|
||||
}
|
||||
0
lib/netutils.h
Normal file → Executable file
0
lib/netutils.h
Normal file → Executable file
261
lib/pairing.c
Executable file
261
lib/pairing.c
Executable file
@@ -0,0 +1,261 @@
|
||||
/**
|
||||
* Copyright (C) 2018 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "pairing.h"
|
||||
#include "curve25519/curve25519.h"
|
||||
#include "ed25519/ed25519.h"
|
||||
#include "crypto.h"
|
||||
|
||||
#define SALT_KEY "Pair-Verify-AES-Key"
|
||||
#define SALT_IV "Pair-Verify-AES-IV"
|
||||
|
||||
struct pairing_s {
|
||||
unsigned char ed_private[64];
|
||||
unsigned char ed_public[32];
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
STATUS_INITIAL,
|
||||
STATUS_SETUP,
|
||||
STATUS_HANDSHAKE,
|
||||
STATUS_FINISHED
|
||||
} status_t;
|
||||
|
||||
struct pairing_session_s {
|
||||
status_t status;
|
||||
|
||||
unsigned char ed_private[64];
|
||||
unsigned char ed_ours[32];
|
||||
unsigned char ed_theirs[32];
|
||||
|
||||
unsigned char ecdh_ours[32];
|
||||
unsigned char ecdh_theirs[32];
|
||||
unsigned char ecdh_secret[32];
|
||||
};
|
||||
|
||||
static int
|
||||
derive_key_internal(pairing_session_t *session, const unsigned char *salt, unsigned int saltlen, unsigned char *key, unsigned int keylen)
|
||||
{
|
||||
unsigned char hash[64];
|
||||
|
||||
if (keylen > sizeof(hash)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
sha_ctx_t *ctx = sha_init();
|
||||
sha_update(ctx, salt, saltlen);
|
||||
sha_update(ctx, session->ecdh_secret, 32);
|
||||
sha_final(ctx, hash, NULL);
|
||||
sha_destroy(ctx);
|
||||
|
||||
memcpy(key, hash, keylen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pairing_t *
|
||||
pairing_init_generate()
|
||||
{
|
||||
unsigned char seed[32];
|
||||
|
||||
if (ed25519_create_seed(seed)) {
|
||||
return NULL;
|
||||
}
|
||||
return pairing_init_seed(seed);
|
||||
}
|
||||
|
||||
pairing_t *
|
||||
pairing_init_seed(const unsigned char seed[32])
|
||||
{
|
||||
pairing_t *pairing;
|
||||
|
||||
pairing = calloc(1, sizeof(pairing_t));
|
||||
if (!pairing) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ed25519_create_keypair(pairing->ed_public, pairing->ed_private, seed);
|
||||
return pairing;
|
||||
}
|
||||
|
||||
void
|
||||
pairing_get_public_key(pairing_t *pairing, unsigned char public_key[32])
|
||||
{
|
||||
assert(pairing);
|
||||
|
||||
memcpy(public_key, pairing->ed_public, 32);
|
||||
}
|
||||
|
||||
void
|
||||
pairing_get_ecdh_secret_key(pairing_session_t *session, unsigned char ecdh_secret[32])
|
||||
{
|
||||
assert(session);
|
||||
memcpy(ecdh_secret, session->ecdh_secret, 32);
|
||||
}
|
||||
|
||||
|
||||
pairing_session_t *
|
||||
pairing_session_init(pairing_t *pairing)
|
||||
{
|
||||
pairing_session_t *session;
|
||||
|
||||
if (!pairing) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
session = calloc(1, sizeof(pairing_session_t));
|
||||
if (!session) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy(session->ed_private, pairing->ed_private, 64);
|
||||
memcpy(session->ed_ours, pairing->ed_public, 32);
|
||||
session->status = STATUS_INITIAL;
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
void
|
||||
pairing_session_set_setup_status(pairing_session_t *session)
|
||||
{
|
||||
assert(session);
|
||||
session->status = STATUS_SETUP;
|
||||
}
|
||||
|
||||
int
|
||||
pairing_session_check_handshake_status(pairing_session_t *session)
|
||||
{
|
||||
assert(session);
|
||||
if (session->status != STATUS_SETUP) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pairing_session_handshake(pairing_session_t *session, const unsigned char ecdh_key[32], const unsigned char ed_key[32])
|
||||
{
|
||||
unsigned char ecdh_priv[32];
|
||||
|
||||
assert(session);
|
||||
|
||||
if (session->status == STATUS_FINISHED) {
|
||||
return -1;
|
||||
}
|
||||
if (ed25519_create_seed(ecdh_priv)) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
memcpy(session->ecdh_theirs, ecdh_key, 32);
|
||||
memcpy(session->ed_theirs, ed_key, 32);
|
||||
curve25519_donna(session->ecdh_ours, ecdh_priv, kCurve25519BasePoint);
|
||||
curve25519_donna(session->ecdh_secret, ecdh_priv, session->ecdh_theirs);
|
||||
|
||||
session->status = STATUS_HANDSHAKE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pairing_session_get_public_key(pairing_session_t *session, unsigned char ecdh_key[32])
|
||||
{
|
||||
assert(session);
|
||||
|
||||
if (session->status != STATUS_HANDSHAKE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(ecdh_key, session->ecdh_ours, 32);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pairing_session_get_signature(pairing_session_t *session, unsigned char signature[64])
|
||||
{
|
||||
unsigned char sig_msg[64];
|
||||
unsigned char key[16];
|
||||
unsigned char iv[16];
|
||||
aes_ctx_t *aes_ctx;
|
||||
|
||||
assert(session);
|
||||
|
||||
if (session->status != STATUS_HANDSHAKE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* First sign the public ECDH keys of both parties */
|
||||
memcpy(&sig_msg[0], session->ecdh_ours, 32);
|
||||
memcpy(&sig_msg[32], session->ecdh_theirs, 32);
|
||||
|
||||
ed25519_sign(signature, sig_msg, sizeof(sig_msg), session->ed_ours, session->ed_private);
|
||||
|
||||
/* Then encrypt the result with keys derived from the shared secret */
|
||||
derive_key_internal(session, (const unsigned char *) SALT_KEY, strlen(SALT_KEY), key, sizeof(key));
|
||||
derive_key_internal(session, (const unsigned char *) SALT_IV, strlen(SALT_IV), iv, sizeof(key));
|
||||
|
||||
aes_ctx = aes_ctr_init(key, iv);
|
||||
aes_ctr_encrypt(aes_ctx, signature, signature, 64);
|
||||
aes_ctr_destroy(aes_ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pairing_session_finish(pairing_session_t *session, const unsigned char signature[64])
|
||||
{
|
||||
unsigned char sig_buffer[64];
|
||||
unsigned char sig_msg[64];
|
||||
unsigned char key[16];
|
||||
unsigned char iv[16];
|
||||
aes_ctx_t *aes_ctx;
|
||||
|
||||
assert(session);
|
||||
|
||||
if (session->status != STATUS_HANDSHAKE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* First decrypt the signature with keys derived from the shared secret */
|
||||
derive_key_internal(session, (const unsigned char *) SALT_KEY, strlen(SALT_KEY), key, sizeof(key));
|
||||
derive_key_internal(session, (const unsigned char *) SALT_IV, strlen(SALT_IV), iv, sizeof(key));
|
||||
|
||||
aes_ctx = aes_ctr_init(key, iv);
|
||||
/* One fake round for the initial handshake encryption */
|
||||
aes_ctr_encrypt(aes_ctx, sig_buffer, sig_buffer, 64);
|
||||
aes_ctr_encrypt(aes_ctx, signature, sig_buffer, 64);
|
||||
aes_ctr_destroy(aes_ctx);
|
||||
|
||||
/* Then verify the signature with public ECDH keys of both parties */
|
||||
memcpy(&sig_msg[0], session->ecdh_theirs, 32);
|
||||
memcpy(&sig_msg[32], session->ecdh_ours, 32);
|
||||
if (!ed25519_verify(sig_buffer, sig_msg, sizeof(sig_msg), session->ed_theirs)) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
session->status = STATUS_FINISHED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
pairing_session_destroy(pairing_session_t *session)
|
||||
{
|
||||
free(session);
|
||||
}
|
||||
|
||||
void
|
||||
pairing_destroy(pairing_t *pairing)
|
||||
{
|
||||
free(pairing);
|
||||
}
|
||||
38
lib/pairing.h
Executable file
38
lib/pairing.h
Executable file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
#ifndef PAIRING_H
|
||||
#define PAIRING_H
|
||||
|
||||
typedef struct pairing_s pairing_t;
|
||||
typedef struct pairing_session_s pairing_session_t;
|
||||
|
||||
pairing_t *pairing_init_generate();
|
||||
pairing_t *pairing_init_seed(const unsigned char seed[32]);
|
||||
void pairing_get_public_key(pairing_t *pairing, unsigned char public_key[32]);
|
||||
|
||||
pairing_session_t *pairing_session_init(pairing_t *pairing);
|
||||
void pairing_session_set_setup_status(pairing_session_t *session);
|
||||
int pairing_session_check_handshake_status(pairing_session_t *session);
|
||||
int pairing_session_handshake(pairing_session_t *session, const unsigned char ecdh_key[32], const unsigned char ed_key[32]);
|
||||
int pairing_session_get_public_key(pairing_session_t *session, unsigned char ecdh_key[32]);
|
||||
int pairing_session_get_signature(pairing_session_t *session, unsigned char signature[64]);
|
||||
int pairing_session_finish(pairing_session_t *session, const unsigned char signature[64]);
|
||||
void pairing_session_destroy(pairing_session_t *session);
|
||||
|
||||
void pairing_destroy(pairing_t *pairing);
|
||||
|
||||
void pairing_get_ecdh_secret_key(pairing_session_t *session, unsigned char ecdh_secret[32]);
|
||||
|
||||
#endif
|
||||
7
lib/playfair/CMakeLists.txt
Executable file
7
lib/playfair/CMakeLists.txt
Executable file
@@ -0,0 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.4.1)
|
||||
aux_source_directory(. playfair_src)
|
||||
set(DIR_SRCS ${playfair_src})
|
||||
include_directories(.)
|
||||
add_library( playfair
|
||||
STATIC
|
||||
${DIR_SRCS})
|
||||
637
lib/playfair/LICENSE.md
Executable file
637
lib/playfair/LICENSE.md
Executable file
@@ -0,0 +1,637 @@
|
||||
|
||||
# GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 [Free Software Foundation, Inc.](http://fsf.org/)
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this license
|
||||
document, but changing it is not allowed.
|
||||
|
||||
## Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for software and
|
||||
other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed to take
|
||||
away your freedom to share and change the works. By contrast, the GNU General
|
||||
Public License is intended to guarantee your freedom to share and change all
|
||||
versions of a program--to make sure it remains free software for all its users.
|
||||
We, the Free Software Foundation, use the GNU General Public License for most
|
||||
of our software; it applies also to any other work released this way by its
|
||||
authors. You can apply it to your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not price. Our
|
||||
General Public Licenses are designed to make sure that you have the freedom to
|
||||
distribute copies of free software (and charge for them if you wish), that you
|
||||
receive source code or can get it if you want it, that you can change the
|
||||
software or use pieces of it in new free programs, and that you know you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you these rights
|
||||
or asking you to surrender the rights. Therefore, you have certain
|
||||
responsibilities if you distribute copies of the software, or if you modify it:
|
||||
responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether gratis or for
|
||||
a fee, you must pass on to the recipients the same freedoms that you received.
|
||||
You must make sure that they, too, receive or can get the source code. And you
|
||||
must show them these terms so they know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
|
||||
1. assert copyright on the software, and
|
||||
2. offer you this License giving you legal permission to copy, distribute
|
||||
and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains that
|
||||
there is no warranty for this free software. For both users' and authors' sake,
|
||||
the GPL requires that modified versions be marked as changed, so that their
|
||||
problems will not be attributed erroneously to authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run modified
|
||||
versions of the software inside them, although the manufacturer can do so. This
|
||||
is fundamentally incompatible with the aim of protecting users' freedom to
|
||||
change the software. The systematic pattern of such abuse occurs in the area of
|
||||
products for individuals to use, which is precisely where it is most
|
||||
unacceptable. Therefore, we have designed this version of the GPL to prohibit
|
||||
the practice for those products. If such problems arise substantially in other
|
||||
domains, we stand ready to extend this provision to those domains in future
|
||||
versions of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents. States
|
||||
should not allow patents to restrict development and use of software on
|
||||
general-purpose computers, but in those that do, we wish to avoid the special
|
||||
danger that patents applied to a free program could make it effectively
|
||||
proprietary. To prevent this, the GPL assures that patents cannot be used to
|
||||
render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and modification
|
||||
follow.
|
||||
|
||||
## TERMS AND CONDITIONS
|
||||
|
||||
### 0. Definitions.
|
||||
|
||||
*This License* refers to version 3 of the GNU General Public License.
|
||||
|
||||
*Copyright* also means copyright-like laws that apply to other kinds of works,
|
||||
such as semiconductor masks.
|
||||
|
||||
*The Program* refers to any copyrightable work licensed under this License.
|
||||
Each licensee is addressed as *you*. *Licensees* and *recipients* may be
|
||||
individuals or organizations.
|
||||
|
||||
To *modify* a work means to copy from or adapt all or part of the work in a
|
||||
fashion requiring copyright permission, other than the making of an exact copy.
|
||||
The resulting work is called a *modified version* of the earlier work or a work
|
||||
*based on* the earlier work.
|
||||
|
||||
A *covered work* means either the unmodified Program or a work based on the
|
||||
Program.
|
||||
|
||||
To *propagate* a work means to do anything with it that, without permission,
|
||||
would make you directly or secondarily liable for infringement under applicable
|
||||
copyright law, except executing it on a computer or modifying a private copy.
|
||||
Propagation includes copying, distribution (with or without modification),
|
||||
making available to the public, and in some countries other activities as well.
|
||||
|
||||
To *convey* a work means any kind of propagation that enables other parties to
|
||||
make or receive copies. Mere interaction with a user through a computer
|
||||
network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays *Appropriate Legal Notices* to the
|
||||
extent that it includes a convenient and prominently visible feature that
|
||||
|
||||
1. displays an appropriate copyright notice, and
|
||||
2. tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the work
|
||||
under this License, and how to view a copy of this License.
|
||||
|
||||
If the interface presents a list of user commands or options, such as a menu, a
|
||||
prominent item in the list meets this criterion.
|
||||
|
||||
### 1. Source Code.
|
||||
|
||||
The *source code* for a work means the preferred form of the work for making
|
||||
modifications to it. *Object code* means any non-source form of a work.
|
||||
|
||||
A *Standard Interface* means an interface that either is an official standard
|
||||
defined by a recognized standards body, or, in the case of interfaces specified
|
||||
for a particular programming language, one that is widely used among developers
|
||||
working in that language.
|
||||
|
||||
The *System Libraries* of an executable work include anything, other than the
|
||||
work as a whole, that (a) is included in the normal form of packaging a Major
|
||||
Component, but which is not part of that Major Component, and (b) serves only
|
||||
to enable use of the work with that Major Component, or to implement a Standard
|
||||
Interface for which an implementation is available to the public in source code
|
||||
form. A *Major Component*, in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system (if any) on
|
||||
which the executable work runs, or a compiler used to produce the work, or an
|
||||
object code interpreter used to run it.
|
||||
|
||||
The *Corresponding Source* for a work in object code form means all the source
|
||||
code needed to generate, install, and (for an executable work) run the object
|
||||
code and to modify the work, including scripts to control those activities.
|
||||
However, it does not include the work's System Libraries, or general-purpose
|
||||
tools or generally available free programs which are used unmodified in
|
||||
performing those activities but which are not part of the work. For example,
|
||||
Corresponding Source includes interface definition files associated with source
|
||||
files for the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require, such as
|
||||
by intimate data communication or control flow between those subprograms and
|
||||
other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users can regenerate
|
||||
automatically from other parts of the Corresponding Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that same work.
|
||||
|
||||
### 2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of copyright on
|
||||
the Program, and are irrevocable provided the stated conditions are met. This
|
||||
License explicitly affirms your unlimited permission to run the unmodified
|
||||
Program. The output from running a covered work is covered by this License only
|
||||
if the output, given its content, constitutes a covered work. This License
|
||||
acknowledges your rights of fair use or other equivalent, as provided by
|
||||
copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not convey, without
|
||||
conditions so long as your license otherwise remains in force. You may convey
|
||||
covered works to others for the sole purpose of having them make modifications
|
||||
exclusively for you, or provide you with facilities for running those works,
|
||||
provided that you comply with the terms of this License in conveying all
|
||||
material for which you do not control copyright. Those thus making or running
|
||||
the covered works for you must do so exclusively on your behalf, under your
|
||||
direction and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under the
|
||||
conditions stated below. Sublicensing is not allowed; section 10 makes it
|
||||
unnecessary.
|
||||
|
||||
### 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological measure
|
||||
under any applicable law fulfilling obligations under article 11 of the WIPO
|
||||
copyright treaty adopted on 20 December 1996, or similar laws prohibiting or
|
||||
restricting circumvention of such measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention is
|
||||
effected by exercising rights under this License with respect to the covered
|
||||
work, and you disclaim any intention to limit operation or modification of the
|
||||
work as a means of enforcing, against the work's users, your or third parties'
|
||||
legal rights to forbid circumvention of technological measures.
|
||||
|
||||
### 4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you receive it,
|
||||
in any medium, provided that you conspicuously and appropriately publish on
|
||||
each copy an appropriate copyright notice; keep intact all notices stating that
|
||||
this License and any non-permissive terms added in accord with section 7 apply
|
||||
to the code; keep intact all notices of the absence of any warranty; and give
|
||||
all recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey, and you may
|
||||
offer support or warranty protection for a fee.
|
||||
|
||||
### 5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to produce it
|
||||
from the Program, in the form of source code under the terms of section 4,
|
||||
provided that you also meet all of these conditions:
|
||||
|
||||
- a) The work must carry prominent notices stating that you modified it, and
|
||||
giving a relevant date.
|
||||
- b) The work must carry prominent notices stating that it is released under
|
||||
this License and any conditions added under section 7. This requirement
|
||||
modifies the requirement in section 4 to *keep intact all notices*.
|
||||
- c) You must license the entire work, as a whole, under this License to
|
||||
anyone who comes into possession of a copy. This License will therefore
|
||||
apply, along with any applicable section 7 additional terms, to the whole
|
||||
of the work, and all its parts, regardless of how they are packaged. This
|
||||
License gives no permission to license the work in any other way, but it
|
||||
does not invalidate such permission if you have separately received it.
|
||||
- d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your work need
|
||||
not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent works,
|
||||
which are not by their nature extensions of the covered work, and which are not
|
||||
combined with it such as to form a larger program, in or on a volume of a
|
||||
storage or distribution medium, is called an *aggregate* if the compilation and
|
||||
its resulting copyright are not used to limit the access or legal rights of the
|
||||
compilation's users beyond what the individual works permit. Inclusion of a
|
||||
covered work in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
### 6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms of sections 4
|
||||
and 5, provided that you also convey the machine-readable Corresponding Source
|
||||
under the terms of this License, in one of these ways:
|
||||
|
||||
- a) Convey the object code in, or embodied in, a physical product (including
|
||||
a physical distribution medium), accompanied by the Corresponding Source
|
||||
fixed on a durable physical medium customarily used for software
|
||||
interchange.
|
||||
- b) Convey the object code in, or embodied in, a physical product (including
|
||||
a physical distribution medium), accompanied by a written offer, valid for
|
||||
at least three years and valid for as long as you offer spare parts or
|
||||
customer support for that product model, to give anyone who possesses the
|
||||
object code either
|
||||
1. a copy of the Corresponding Source for all the software in the product
|
||||
that is covered by this License, on a durable physical medium
|
||||
customarily used for software interchange, for a price no more than your
|
||||
reasonable cost of physically performing this conveying of source, or
|
||||
2. access to copy the Corresponding Source from a network server at no
|
||||
charge.
|
||||
- c) Convey individual copies of the object code with a copy of the written
|
||||
offer to provide the Corresponding Source. This alternative is allowed only
|
||||
occasionally and noncommercially, and only if you received the object code
|
||||
with such an offer, in accord with subsection 6b.
|
||||
- d) Convey the object code by offering access from a designated place
|
||||
(gratis or for a charge), and offer equivalent access to the Corresponding
|
||||
Source in the same way through the same place at no further charge. You
|
||||
need not require recipients to copy the Corresponding Source along with the
|
||||
object code. If the place to copy the object code is a network server, the
|
||||
Corresponding Source may be on a different server operated by you or a
|
||||
third party) that supports equivalent copying facilities, provided you
|
||||
maintain clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the Corresponding
|
||||
Source, you remain obligated to ensure that it is available for as long as
|
||||
needed to satisfy these requirements.
|
||||
- e) Convey the object code using peer-to-peer transmission, provided you
|
||||
inform other peers where the object code and Corresponding Source of the
|
||||
work are being offered to the general public at no charge under subsection
|
||||
6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded from the
|
||||
Corresponding Source as a System Library, need not be included in conveying the
|
||||
object code work.
|
||||
|
||||
A *User Product* is either
|
||||
|
||||
1. a *consumer product*, which means any tangible personal property which is
|
||||
normally used for personal, family, or household purposes, or
|
||||
2. anything designed or sold for incorporation into a dwelling.
|
||||
|
||||
In determining whether a product is a consumer product, doubtful cases shall be
|
||||
resolved in favor of coverage. For a particular product received by a
|
||||
particular user, *normally used* refers to a typical or common use of that
|
||||
class of product, regardless of the status of the particular user or of the way
|
||||
in which the particular user actually uses, or expects or is expected to use,
|
||||
the product. A product is a consumer product regardless of whether the product
|
||||
has substantial commercial, industrial or non-consumer uses, unless such uses
|
||||
represent the only significant mode of use of the product.
|
||||
|
||||
*Installation Information* for a User Product means any methods, procedures,
|
||||
authorization keys, or other information required to install and execute
|
||||
modified versions of a covered work in that User Product from a modified
|
||||
version of its Corresponding Source. The information must suffice to ensure
|
||||
that the continued functioning of the modified object code is in no case
|
||||
prevented or interfered with solely because modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as part of a
|
||||
transaction in which the right of possession and use of the User Product is
|
||||
transferred to the recipient in perpetuity or for a fixed term (regardless of
|
||||
how the transaction is characterized), the Corresponding Source conveyed under
|
||||
this section must be accompanied by the Installation Information. But this
|
||||
requirement does not apply if neither you nor any third party retains the
|
||||
ability to install modified object code on the User Product (for example, the
|
||||
work has been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates for a
|
||||
work that has been modified or installed by the recipient, or for the User
|
||||
Product in which it has been modified or installed. Access to a network may be
|
||||
denied when the modification itself materially and adversely affects the
|
||||
operation of the network or violates the rules and protocols for communication
|
||||
across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided, in accord
|
||||
with this section must be in a format that is publicly documented (and with an
|
||||
implementation available to the public in source code form), and must require
|
||||
no special password or key for unpacking, reading or copying.
|
||||
|
||||
### 7. Additional Terms.
|
||||
|
||||
*Additional permissions* are terms that supplement the terms of this License by
|
||||
making exceptions from one or more of its conditions. Additional permissions
|
||||
that are applicable to the entire Program shall be treated as though they were
|
||||
included in this License, to the extent that they are valid under applicable
|
||||
law. If additional permissions apply only to part of the Program, that part may
|
||||
be used separately under those permissions, but the entire Program remains
|
||||
governed by this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option remove any
|
||||
additional permissions from that copy, or from any part of it. (Additional
|
||||
permissions may be written to require their own removal in certain cases when
|
||||
you modify the work.) You may place additional permissions on material, added
|
||||
by you to a covered work, for which you have or can give appropriate copyright
|
||||
permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you add to a
|
||||
covered work, you may (if authorized by the copyright holders of that material)
|
||||
supplement the terms of this License with terms:
|
||||
|
||||
- a) Disclaiming warranty or limiting liability differently from the terms of
|
||||
sections 15 and 16 of this License; or
|
||||
- b) Requiring preservation of specified reasonable legal notices or author
|
||||
attributions in that material or in the Appropriate Legal Notices displayed
|
||||
by works containing it; or
|
||||
- c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in reasonable
|
||||
ways as different from the original version; or
|
||||
- d) Limiting the use for publicity purposes of names of licensors or authors
|
||||
of the material; or
|
||||
- e) Declining to grant rights under trademark law for use of some trade
|
||||
names, trademarks, or service marks; or
|
||||
- f) Requiring indemnification of licensors and authors of that material by
|
||||
anyone who conveys the material (or modified versions of it) with
|
||||
contractual assumptions of liability to the recipient, for any liability
|
||||
that these contractual assumptions directly impose on those licensors and
|
||||
authors.
|
||||
|
||||
All other non-permissive additional terms are considered *further restrictions*
|
||||
within the meaning of section 10. If the Program as you received it, or any
|
||||
part of it, contains a notice stating that it is governed by this License along
|
||||
with a term that is a further restriction, you may remove that term. If a
|
||||
license document contains a further restriction but permits relicensing or
|
||||
conveying under this License, you may add to a covered work material governed
|
||||
by the terms of that license document, provided that the further restriction
|
||||
does not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you must place,
|
||||
in the relevant source files, a statement of the additional terms that apply to
|
||||
those files, or a notice indicating where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the form of a
|
||||
separately written license, or stated as exceptions; the above requirements
|
||||
apply either way.
|
||||
|
||||
### 8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly provided
|
||||
under this License. Any attempt otherwise to propagate or modify it is void,
|
||||
and will automatically terminate your rights under this License (including any
|
||||
patent licenses granted under the third paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your license from a
|
||||
particular copyright holder is reinstated
|
||||
|
||||
- a) provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and
|
||||
- b) permanently, if the copyright holder fails to notify you of the
|
||||
violation by some reasonable means prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is reinstated
|
||||
permanently if the copyright holder notifies you of the violation by some
|
||||
reasonable means, this is the first time you have received notice of violation
|
||||
of this License (for any work) from that copyright holder, and you cure the
|
||||
violation prior to 30 days after your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the licenses
|
||||
of parties who have received copies or rights from you under this License. If
|
||||
your rights have been terminated and not permanently reinstated, you do not
|
||||
qualify to receive new licenses for the same material under section 10.
|
||||
|
||||
### 9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or run a copy
|
||||
of the Program. Ancillary propagation of a covered work occurring solely as a
|
||||
consequence of using peer-to-peer transmission to receive a copy likewise does
|
||||
not require acceptance. However, nothing other than this License grants you
|
||||
permission to propagate or modify any covered work. These actions infringe
|
||||
copyright if you do not accept this License. Therefore, by modifying or
|
||||
propagating a covered work, you indicate your acceptance of this License to do
|
||||
so.
|
||||
|
||||
### 10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically receives a
|
||||
license from the original licensors, to run, modify and propagate that work,
|
||||
subject to this License. You are not responsible for enforcing compliance by
|
||||
third parties with this License.
|
||||
|
||||
An *entity transaction* is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered work
|
||||
results from an entity transaction, each party to that transaction who receives
|
||||
a copy of the work also receives whatever licenses to the work the party's
|
||||
predecessor in interest had or could give under the previous paragraph, plus a
|
||||
right to possession of the Corresponding Source of the work from the
|
||||
predecessor in interest, if the predecessor has it or can get it with
|
||||
reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the rights
|
||||
granted or affirmed under this License. For example, you may not impose a
|
||||
license fee, royalty, or other charge for exercise of rights granted under this
|
||||
License, and you may not initiate litigation (including a cross-claim or
|
||||
counterclaim in a lawsuit) alleging that any patent claim is infringed by
|
||||
making, using, selling, offering for sale, or importing the Program or any
|
||||
portion of it.
|
||||
|
||||
### 11. Patents.
|
||||
|
||||
A *contributor* is a copyright holder who authorizes use under this License of
|
||||
the Program or a work on which the Program is based. The work thus licensed is
|
||||
called the contributor's *contributor version*.
|
||||
|
||||
A contributor's *essential patent claims* are all patent claims owned or
|
||||
controlled by the contributor, whether already acquired or hereafter acquired,
|
||||
that would be infringed by some manner, permitted by this License, of making,
|
||||
using, or selling its contributor version, but do not include claims that would
|
||||
be infringed only as a consequence of further modification of the contributor
|
||||
version. For purposes of this definition, *control* includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of this
|
||||
License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free patent
|
||||
license under the contributor's essential patent claims, to make, use, sell,
|
||||
offer for sale, import and otherwise run, modify and propagate the contents of
|
||||
its contributor version.
|
||||
|
||||
In the following three paragraphs, a *patent license* is any express agreement
|
||||
or commitment, however denominated, not to enforce a patent (such as an express
|
||||
permission to practice a patent or covenant not to sue for patent
|
||||
infringement). To *grant* such a patent license to a party means to make such
|
||||
an agreement or commitment not to enforce a patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license, and the
|
||||
Corresponding Source of the work is not available for anyone to copy, free of
|
||||
charge and under the terms of this License, through a publicly available
|
||||
network server or other readily accessible means, then you must either
|
||||
|
||||
1. cause the Corresponding Source to be so available, or
|
||||
2. arrange to deprive yourself of the benefit of the patent license for this
|
||||
particular work, or
|
||||
3. arrange, in a manner consistent with the requirements of this License, to
|
||||
extend the patent license to downstream recipients.
|
||||
|
||||
*Knowingly relying* means you have actual knowledge that, but for the patent
|
||||
license, your conveying the covered work in a country, or your recipient's use
|
||||
of the covered work in a country, would infringe one or more identifiable
|
||||
patents in that country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or arrangement, you
|
||||
convey, or propagate by procuring conveyance of, a covered work, and grant a
|
||||
patent license to some of the parties receiving the covered work authorizing
|
||||
them to use, propagate, modify or convey a specific copy of the covered work,
|
||||
then the patent license you grant is automatically extended to all recipients
|
||||
of the covered work and works based on it.
|
||||
|
||||
A patent license is *discriminatory* if it does not include within the scope of
|
||||
its coverage, prohibits the exercise of, or is conditioned on the non-exercise
|
||||
of one or more of the rights that are specifically granted under this License.
|
||||
You may not convey a covered work if you are a party to an arrangement with a
|
||||
third party that is in the business of distributing software, under which you
|
||||
make payment to the third party based on the extent of your activity of
|
||||
conveying the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory patent
|
||||
license
|
||||
|
||||
- a) in connection with copies of the covered work conveyed by you (or copies
|
||||
made from those copies), or
|
||||
- b) primarily for and in connection with specific products or compilations
|
||||
that contain the covered work, unless you entered into that arrangement, or
|
||||
that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting any implied
|
||||
license or other defenses to infringement that may otherwise be available to
|
||||
you under applicable patent law.
|
||||
|
||||
### 12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not excuse
|
||||
you from the conditions of this License. If you cannot convey a covered work so
|
||||
as to satisfy simultaneously your obligations under this License and any other
|
||||
pertinent obligations, then as a consequence you may not convey it at all. For
|
||||
example, if you agree to terms that obligate you to collect a royalty for
|
||||
further conveying from those to whom you convey the Program, the only way you
|
||||
could satisfy both those terms and this License would be to refrain entirely
|
||||
from conveying the Program.
|
||||
|
||||
### 13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have permission to
|
||||
link or combine any covered work with a work licensed under version 3 of the
|
||||
GNU Affero General Public License into a single combined work, and to convey
|
||||
the resulting work. The terms of this License will continue to apply to the
|
||||
part which is the covered work, but the special requirements of the GNU Affero
|
||||
General Public License, section 13, concerning interaction through a network
|
||||
will apply to the combination as such.
|
||||
|
||||
### 14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of the GNU
|
||||
General Public License from time to time. Such new versions will be similar in
|
||||
spirit to the present version, but may differ in detail to address new problems
|
||||
or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program specifies
|
||||
that a certain numbered version of the GNU General Public License *or any later
|
||||
version* applies to it, you have the option of following the terms and
|
||||
conditions either of that numbered version or of any later version published by
|
||||
the Free Software Foundation. If the Program does not specify a version number
|
||||
of the GNU General Public License, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future versions of the
|
||||
GNU General Public License can be used, that proxy's public statement of
|
||||
acceptance of a version permanently authorizes you to choose that version for
|
||||
the Program.
|
||||
|
||||
Later license versions may give you additional or different permissions.
|
||||
However, no additional obligations are imposed on any author or copyright
|
||||
holder as a result of your choosing to follow a later version.
|
||||
|
||||
### 15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE
|
||||
LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER
|
||||
PARTIES PROVIDE THE PROGRAM *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE
|
||||
QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
|
||||
DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
|
||||
CORRECTION.
|
||||
|
||||
### 16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY
|
||||
COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS
|
||||
PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
|
||||
INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
|
||||
THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
|
||||
INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
|
||||
PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY
|
||||
HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
### 17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided above cannot
|
||||
be given local legal effect according to their terms, reviewing courts shall
|
||||
apply local law that most closely approximates an absolute waiver of all civil
|
||||
liability in connection with the Program, unless a warranty or assumption of
|
||||
liability accompanies a copy of the Program in return for a fee.
|
||||
|
||||
## END OF TERMS AND CONDITIONS ###
|
||||
|
||||
### How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest possible
|
||||
use to the public, the best way to achieve this is to make it free software
|
||||
which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest to attach
|
||||
them to the start of each source file to most effectively state the exclusion
|
||||
of warranty; and each file should have at least the *copyright* line and a
|
||||
pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short notice like
|
||||
this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w` and `show c` should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands might
|
||||
be different; for a GUI interface, you would use an *about box*.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school, if
|
||||
any, to sign a *copyright disclaimer* for the program, if necessary. For more
|
||||
information on this, and how to apply and follow the GNU GPL, see
|
||||
[http://www.gnu.org/licenses/](http://www.gnu.org/licenses/).
|
||||
|
||||
The GNU General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may consider
|
||||
it more useful to permit linking proprietary applications with the library. If
|
||||
this is what you want to do, use the GNU Lesser General Public License instead
|
||||
of this License. But first, please read
|
||||
[http://www.gnu.org/philosophy/why-not-lgpl.html](http://www.gnu.org/philosophy/why-not-lgpl.html).
|
||||
443
lib/playfair/hand_garble.c
Executable file
443
lib/playfair/hand_garble.c
Executable file
@@ -0,0 +1,443 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define printf(...) (void)0;
|
||||
|
||||
uint8_t rol8(uint8_t x, int y);
|
||||
uint32_t rol8x(uint8_t x, int y);
|
||||
|
||||
uint32_t weird_ror8(uint8_t input, int count)
|
||||
{
|
||||
if (count == 0)
|
||||
return 0;
|
||||
return ((input >> count) & 0xff) | (input & 0xff) << (8-count);
|
||||
|
||||
}
|
||||
|
||||
uint32_t weird_rol8(uint8_t input, int count)
|
||||
{
|
||||
if (count == 0)
|
||||
return 0;
|
||||
return ((input << count) & 0xff) | (input & 0xff) >> (8-count);
|
||||
}
|
||||
|
||||
uint32_t weird_rol32(uint8_t input, int count)
|
||||
{
|
||||
if (count == 0)
|
||||
return 0;
|
||||
return (input << count) ^ (input >> (8 - count));
|
||||
}
|
||||
|
||||
// I do not know why it is doing all of this, and there is still a possibility for a gremlin or two to be lurking in the background
|
||||
// I DO know it is not trivial. It could be purely random garbage, of course.
|
||||
void garble(unsigned char* buffer0, unsigned char* buffer1, unsigned char* buffer2, unsigned char* buffer3, unsigned char* buffer4)
|
||||
{
|
||||
unsigned int tmp, tmp2, tmp3;
|
||||
unsigned int A, B, C, D, E, M, J, G, F, H, K, R, S, T, U, V, W, X, Y, Z;
|
||||
// buffer1[64] = A
|
||||
// (buffer1[99] / 3) = B
|
||||
// 0ABAAABB
|
||||
// Then we AND with a complex expression, and add 20 just for good measure
|
||||
buffer2[12] = 0x14 + (((buffer1[64] & 92) | ((buffer1[99] / 3) & 35)) & buffer4[rol8x(buffer4[(buffer1[206] % 21)],4) % 21]);
|
||||
printf("buffer2[12] = %02x\n", buffer2[12]);
|
||||
|
||||
// This is a bit simpler: 2*B*B/25
|
||||
buffer1[4] = (buffer1[99] / 5) * (buffer1[99] / 5) * 2;
|
||||
printf("buffer1[4] = %02x\n", buffer1[4]);
|
||||
|
||||
// Simpler still!
|
||||
buffer2[34] = 0xb8;
|
||||
printf("buffer2[34] = %02x\n", buffer2[34]);
|
||||
|
||||
// ...
|
||||
buffer1[153] ^= (buffer2[buffer1[203] % 35] * buffer2[buffer1[203] % 35] * buffer1[190]);
|
||||
printf("buffer1[153] = %02x\n", buffer1[153]);
|
||||
|
||||
// This one looks simple, but wow was it not :(
|
||||
buffer0[3] -= (((buffer4[buffer1[205] % 21]>>1) & 80) | 0xe6440);
|
||||
printf("buffer0[3] = %02x\n", buffer0[3]);
|
||||
|
||||
// This is always 0x93
|
||||
buffer0[16] = 0x93;
|
||||
printf("buffer0[16] = %02x\n", buffer0[16]);
|
||||
|
||||
// This is always 0x62
|
||||
buffer0[13] = 0x62;
|
||||
printf("buffer0[13] = %02x\n", buffer0[13]);
|
||||
|
||||
buffer1[33] -= (buffer4[buffer1[36] % 21] & 0xf6);
|
||||
printf("buffer1[33] = %02x\n", buffer1[33]);
|
||||
|
||||
// This is always 7
|
||||
tmp2 = buffer2[buffer1[67] % 35];
|
||||
buffer2[12] = 0x07;
|
||||
printf("buffer2[12] = %02x\n", buffer2[12]);
|
||||
|
||||
// This is pretty easy!
|
||||
tmp = buffer0[buffer1[181] % 20];
|
||||
buffer1[2] -= 3136;
|
||||
printf("buffer1[2] = %02x\n", buffer1[2]);
|
||||
|
||||
buffer0[19] = buffer4[buffer1[58] % 21];
|
||||
printf("buffer0[19] = %02x\n", buffer0[19]);
|
||||
|
||||
buffer3[0] = 92 - buffer2[buffer1[32] % 35];
|
||||
printf("buffer3[0] = %02x\n", buffer3[0]);
|
||||
|
||||
buffer3[4] = buffer2[buffer1[15] % 35] + 0x9e;
|
||||
printf("buffer3[4] = %02x\n", buffer3[4]);
|
||||
|
||||
buffer1[34] += (buffer4[((buffer2[buffer1[15] % 35] + 0x9e) & 0xff) % 21] / 5);
|
||||
printf("buffer1[34] = %02x\n", buffer1[34]);
|
||||
|
||||
buffer0[19] += 0xfffffee6 - ((buffer0[buffer3[4] % 20]>>1) & 102);
|
||||
printf("buffer0[19] = %02x\n", buffer0[19]);
|
||||
|
||||
// This LOOKS like it should be a rol8x, but it just doesnt work out because if the shift amount is 0, then the output is 0 too :(
|
||||
// FIXME: Switch to weird_ror8
|
||||
buffer1[15] = (3*(((buffer1[72] >> (buffer4[buffer1[190] % 21] & 7)) ^ (buffer1[72] << ((7 - (buffer4[buffer1[190] % 21]-1)&7)))) - (3*buffer4[buffer1[126] % 21]))) ^ buffer1[15];
|
||||
printf("buffer1[15] = %02x\n", buffer1[15]);
|
||||
|
||||
buffer0[15] ^= buffer2[buffer1[181] % 35] * buffer2[buffer1[181] % 35] * buffer2[buffer1[181] % 35];
|
||||
printf("buffer0[15] = %02x\n", buffer0[15]);
|
||||
|
||||
buffer2[4] ^= buffer1[202]/3;
|
||||
printf("buffer2[4] = %02x\n", buffer2[4]);
|
||||
|
||||
// This could probably be quite a bit simpler.
|
||||
A = 92 - buffer0[buffer3[0] % 20];
|
||||
E = (A & 0xc6) | (~buffer1[105] & 0xc6) | (A & (~buffer1[105]));
|
||||
buffer2[1] += (E*E*E);
|
||||
printf("buffer2[1] = %02x\n", buffer2[1]);
|
||||
|
||||
buffer0[19] ^= ((224 | (buffer4[buffer1[92] % 21] & 27)) * buffer2[buffer1[41] % 35]) / 3;
|
||||
printf("buffer0[19] = %02x\n", buffer0[19]);
|
||||
|
||||
buffer1[140] += weird_ror8(92, buffer1[5] & 7);
|
||||
printf("buffer1[140] = %02x\n", buffer1[140]);
|
||||
|
||||
// Is this as simple as it could be?
|
||||
buffer2[12] += ((((~buffer1[4]) ^ buffer2[buffer1[12] % 35]) | buffer1[182]) & 192) | (((~buffer1[4]) ^ buffer2[buffer1[12] % 35]) & buffer1[182]);
|
||||
printf("buffer2[12] = %02x\n", buffer2[12]);
|
||||
|
||||
buffer1[36] += 125;
|
||||
printf("buffer1[36] = %02x\n", buffer1[36]);
|
||||
|
||||
buffer1[124] = rol8x((((74 & buffer1[138]) | ((74 | buffer1[138]) & buffer0[15])) & buffer0[buffer1[43] % 20]) | (((74 & buffer1[138]) | ((74 | buffer1[138]) & buffer0[15]) | buffer0[buffer1[43] % 20]) & 95), 4);
|
||||
printf("buffer1[124] = %02x\n", buffer1[124]);
|
||||
|
||||
buffer3[8] = ((((buffer0[buffer3[4] % 20] & 95)) & ((buffer4[buffer1[68] % 21] & 46) << 1)) | 16) ^ 92;
|
||||
printf("buffer3[8] = %02x\n", buffer3[8]);
|
||||
|
||||
A = buffer1[177] + buffer4[buffer1[79] % 21];
|
||||
D = (((A >> 1) | ((3 * buffer1[148]) / 5)) & buffer2[1]) | ((A >> 1) & ((3 * buffer1[148])/5));
|
||||
buffer3[12] = ((-34 - D));
|
||||
printf("buffer3[12] = %02x\n", buffer3[12]);
|
||||
|
||||
A = 8 - ((buffer2[22] & 7)); // FIXME: buffer2[22] = 74, so A is always 6 and B^C is just ror8(buffer1[33], 6)
|
||||
B = (buffer1[33] >> (A & 7));
|
||||
C = buffer1[33] << (buffer2[22] & 7);
|
||||
buffer2[16] += ((buffer2[buffer3[0] % 35] & 159) | buffer0[buffer3[4] % 20] | 8) - ((B^C) | 128);
|
||||
printf("buffer2[16] = %02x\n", buffer2[16]);
|
||||
|
||||
// This one was very easy so I just skipped ahead and did it
|
||||
buffer0[14] ^= buffer2[buffer3[12] % 35];
|
||||
printf("buffer0[14] = %02x\n", buffer0[14]);
|
||||
|
||||
// Monster goes here
|
||||
A = weird_rol8(buffer4[buffer0[buffer1[201] % 20] % 21], ((buffer2[buffer1[112] % 35] << 1) & 7));
|
||||
D = (buffer0[buffer1[208] % 20] & 131) | (buffer0[buffer1[164] % 20] & 124);
|
||||
buffer1[19] += (A & (D/5)) | ((A | (D/5)) & 37);
|
||||
printf("buffer1[19] = %02x\n", buffer1[19]);
|
||||
|
||||
buffer2[8] = weird_ror8(140, ((buffer4[buffer1[45] % 21] + 92) * (buffer4[buffer1[45] % 21] + 92)) & 7);
|
||||
printf("buffer2[8] = %02x\n", buffer2[8]);
|
||||
|
||||
buffer1[190] = 56;
|
||||
printf("buffer1[190] = %02x\n", buffer1[190]);
|
||||
|
||||
buffer2[8] ^= buffer3[0];
|
||||
printf("buffer2[8] = %02x\n", buffer2[8]);
|
||||
|
||||
buffer1[53] = ~((buffer0[buffer1[83] % 20] | 204)/5);
|
||||
printf("buffer1[53] = %02x\n", buffer1[53]);
|
||||
|
||||
buffer0[13] += buffer0[buffer1[41] % 20];
|
||||
printf("buffer0[13] = %02x\n", buffer0[13]);
|
||||
|
||||
buffer0[10] = ((buffer2[buffer3[0] % 35] & buffer1[2]) | ((buffer2[buffer3[0] % 35] | buffer1[2]) & buffer3[12])) / 15;
|
||||
printf("buffer0[10] = %02x\n", buffer0[10]);
|
||||
|
||||
A = (((56 | (buffer4[buffer1[2] % 21] & 68)) | buffer2[buffer3[8] % 35]) & 42) | (((buffer4[buffer1[2] % 21] & 68) | 56) & buffer2[buffer3[8] % 35]);
|
||||
buffer3[16] = (A*A) + 110;
|
||||
printf("buffer3[16] = %02x\n", buffer3[16]);
|
||||
|
||||
buffer3[20] = 202 - buffer3[16];
|
||||
printf("buffer3[20] = %02x\n", buffer3[20]);
|
||||
|
||||
buffer3[24] = buffer1[151];
|
||||
printf("buffer3[24] = %02x\n", buffer3[24]);
|
||||
|
||||
buffer2[13] ^= buffer4[buffer3[0] % 21];
|
||||
printf("buffer2[13] = %02x\n", buffer2[13]);
|
||||
|
||||
B = ((buffer2[buffer1[179] % 35] - 38) & 177) | (buffer3[12] & 177);
|
||||
C = ((buffer2[buffer1[179] % 35] - 38)) & buffer3[12];
|
||||
buffer3[28] = 30 + ((B | C) * (B | C));
|
||||
printf("buffer3[28] = %02x\n", buffer3[28]);
|
||||
|
||||
buffer3[32] = buffer3[28] + 62;
|
||||
printf("buffer3[32] = %02x\n", buffer3[32]);
|
||||
|
||||
// eek
|
||||
A = ((buffer3[20] + (buffer3[0] & 74)) | ~buffer4[buffer3[0] % 21]) & 121;
|
||||
B = ((buffer3[20] + (buffer3[0] & 74)) & ~buffer4[buffer3[0] % 21]);
|
||||
tmp3 = (A|B);
|
||||
C = ((((A|B) ^ 0xffffffa6) | buffer3[0]) & 4) | (((A|B) ^ 0xffffffa6) & buffer3[0]);
|
||||
buffer1[47] = (buffer2[buffer1[89] % 35] + C) ^ buffer1[47];
|
||||
printf("buffer1[47] = %02x\n", buffer1[47]);
|
||||
|
||||
buffer3[36] = ((rol8((tmp & 179) + 68, 2) & buffer0[3]) | (tmp2 & ~buffer0[3])) - 15;
|
||||
printf("buffer3[36] = %02x\n", buffer3[36]);
|
||||
|
||||
buffer1[123] ^= 221;
|
||||
printf("buffer1[123] = %02x\n", buffer1[123]);
|
||||
|
||||
A = ((buffer4[buffer3[0] % 21]) / 3) - buffer2[buffer3[4] % 35];
|
||||
C = (((buffer3[0] & 163) + 92) & 246) | (buffer3[0] & 92);
|
||||
E = ((C | buffer3[24]) & 54) | (C & buffer3[24]);
|
||||
buffer3[40] = A - E;
|
||||
printf("buffer3[40] = %02x\n", buffer3[40]);
|
||||
|
||||
buffer3[44] = tmp3 ^ 81 ^ (((buffer3[0] >> 1) & 101) + 26);
|
||||
printf("buffer3[44] = %02x\n", buffer3[44]);
|
||||
|
||||
buffer3[48] = buffer2[buffer3[4] % 35] & 27;
|
||||
printf("buffer3[48] = %02x\n", buffer3[48]);
|
||||
buffer3[52] = 27;
|
||||
printf("buffer3[52] = %02x\n", buffer3[52]);
|
||||
buffer3[56] = 199;
|
||||
printf("buffer3[56] = %02x\n", buffer3[56]);
|
||||
|
||||
// caffeine
|
||||
buffer3[64] = buffer3[4] + (((((((buffer3[40] | buffer3[24]) & 177) | (buffer3[40] & buffer3[24])) & ((((buffer4[buffer3[0] % 20] & 177) | 176)) | ((buffer4[buffer3[0] % 21]) & ~3))) | ((((buffer3[40] & buffer3[24]) | ((buffer3[40] | buffer3[24]) & 177)) & 199) | ((((buffer4[buffer3[0] % 21] & 1) + 176) | (buffer4[buffer3[0] % 21] & ~3)) & buffer3[56]))) & (~buffer3[52])) | buffer3[48]);
|
||||
printf("buffer3[64] = %02x (want E7)\n", buffer3[64]);
|
||||
|
||||
buffer2[33] ^= buffer1[26];
|
||||
printf("buffer2[33] = %02x\n", buffer2[33]);
|
||||
|
||||
buffer1[106] ^= buffer3[20] ^ 133;
|
||||
printf("buffer1[106] = %02x\n", buffer1[106]);
|
||||
|
||||
buffer2[30] = ((buffer3[64] / 3) - (275 | (buffer3[0] & 247))) ^ buffer0[buffer1[122] % 20];
|
||||
printf("buffer2[130] = %02x\n", buffer2[30]);
|
||||
|
||||
buffer1[22] = (buffer2[buffer1[90] % 35] & 95) | 68;
|
||||
printf("buffer1[22] = %02x\n", buffer1[22]);
|
||||
|
||||
A = (buffer4[buffer3[36] % 21] & 184) | (buffer2[buffer3[44] % 35] & ~184);
|
||||
buffer2[18] += ((A*A*A) >> 1);
|
||||
printf("buffer2[18] = %02x\n", buffer2[18]);
|
||||
|
||||
buffer2[5] -= buffer4[buffer1[92] % 21];
|
||||
printf("buffer2[5] = %02x\n", buffer2[5]);
|
||||
|
||||
A = (((buffer1[41] & ~24)|(buffer2[buffer1[183] % 35] & 24)) & (buffer3[16] + 53)) | (buffer3[20] & buffer2[buffer3[20] % 35]);
|
||||
B = (buffer1[17] & (~buffer3[44])) | (buffer0[buffer1[59] % 20] & buffer3[44]);
|
||||
buffer2[18] ^= (A*B);
|
||||
printf("buffer2[18] = %02x\n", buffer2[18]);
|
||||
|
||||
|
||||
A = weird_ror8(buffer1[11], buffer2[buffer1[28] % 35] & 7) & 7;
|
||||
B = (((buffer0[buffer1[93] % 20] & ~buffer0[14]) | (buffer0[14] & 150)) & ~28) | (buffer1[7] & 28);
|
||||
buffer2[22] = (((((B | weird_rol8(buffer2[buffer3[0] % 35], A)) & buffer2[33]) | (B & weird_rol8(buffer2[buffer3[0] % 35], A))) + 74) & 0xff);
|
||||
printf("buffer2[22] = %02x\n", buffer2[22]);
|
||||
|
||||
A = buffer4[(buffer0[buffer1[39] % 20] ^ 217) % 21]; // X5
|
||||
buffer0[15] -= ((((buffer3[20] | buffer3[0]) & 214) | (buffer3[20] & buffer3[0])) & A) | ((((buffer3[20] | buffer3[0]) & 214) | (buffer3[20] & buffer3[0]) | A) & buffer3[32]);
|
||||
printf("buffer0[15] = %02x\n", buffer0[15]);
|
||||
|
||||
// We need to save T here, and boy is it complicated to calculate!
|
||||
B = (((buffer2[buffer1[57] % 35] & buffer0[buffer3[64] % 20]) | ((buffer0[buffer3[64] % 20] | buffer2[buffer1[57] % 35]) & 95) | (buffer3[64] & 45) | 82) & 32);
|
||||
C = ((buffer2[buffer1[57] % 35] & buffer0[buffer3[64] % 20]) | ((buffer2[buffer1[57] % 35] | buffer0[buffer3[64] % 20]) & 95)) & ((buffer3[64] & 45) | 82);
|
||||
D = ((((buffer3[0]/3) - (buffer3[64]|buffer1[22]))) ^ (buffer3[28] + 62) ^ ((B|C)));
|
||||
T = buffer0[(D & 0xff) % 20];
|
||||
|
||||
buffer3[68] = (buffer0[buffer1[99] % 20] * buffer0[buffer1[99] % 20] * buffer0[buffer1[99] % 20] * buffer0[buffer1[99] % 20]) | buffer2[buffer3[64] % 35];
|
||||
printf("buffer3[68] = %02x\n", buffer3[68]);
|
||||
|
||||
U = buffer0[buffer1[50] % 20]; // this is also v100
|
||||
W = buffer2[buffer1[138] % 35];
|
||||
X = buffer4[buffer1[39] % 21];
|
||||
Y = buffer0[buffer1[4] % 20]; // this is also v120
|
||||
Z = buffer4[buffer1[202] % 21]; // also v124
|
||||
V = buffer0[buffer1[151] % 20];
|
||||
S = buffer2[buffer1[14] % 35];
|
||||
R = buffer0[buffer1[145] % 20];
|
||||
|
||||
A = (buffer2[buffer3[68] % 35] & buffer0[buffer1[209] % 20]) | ((buffer2[buffer3[68] % 35] | buffer0[buffer1[209] % 20]) & 24);
|
||||
B = weird_rol8(buffer4[buffer1[127] % 21], buffer2[buffer3[68] % 35] & 7);
|
||||
C = (A & buffer0[10]) | (B & ~buffer0[10]);
|
||||
D = 7 ^ (buffer4[buffer2[buffer3[36] % 35] % 21] << 1);
|
||||
buffer3[72] = (C & 71) | (D & ~71);
|
||||
printf("buffer3[72] = %02x\n", buffer3[72]);
|
||||
|
||||
buffer2[2] += (((buffer0[buffer3[20] % 20] << 1) & 159) | (buffer4[buffer1[190] % 21] & ~159)) & ((((buffer4[buffer3[64] % 21] & 110) | (buffer0[buffer1[25] % 20] & ~110)) & ~150) | (buffer1[25] & 150));
|
||||
printf("buffer2[2] = %02x\n", buffer2[2]);
|
||||
|
||||
buffer2[14] -= ((buffer2[buffer3[20] % 35] & (buffer3[72] ^ buffer2[buffer1[100] % 35])) & ~34) | (buffer1[97] & 34);
|
||||
printf("buffer2[14] = %02x\n", buffer2[14]);
|
||||
|
||||
buffer0[17] = 115;
|
||||
printf("buffer0[17] = %02x\n", buffer0[17]);
|
||||
|
||||
buffer1[23] ^= ((((((buffer4[buffer1[17] % 21] | buffer0[buffer3[20] % 20]) & buffer3[72]) | (buffer4[buffer1[17] % 21] & buffer0[buffer3[20] % 20])) & (buffer1[50]/3)) |
|
||||
((((buffer4[buffer1[17] % 21] | buffer0[buffer3[20] % 20]) & buffer3[72]) | (buffer4[buffer1[17] % 21] & buffer0[buffer3[20] % 20]) | (buffer1[50] / 3)) & 246)) << 1);
|
||||
printf("buffer1[23] = %02x\n", buffer1[23]);
|
||||
|
||||
buffer0[13] = ((((((buffer0[buffer3[40] % 20] | buffer1[10]) & 82) | (buffer0[buffer3[40] % 20] & buffer1[10])) & 209) |
|
||||
((buffer0[buffer1[39] % 20] << 1) & 46)) >> 1);
|
||||
printf("buffer0[13] = %02x\n", buffer0[13]);
|
||||
|
||||
buffer2[33] -= buffer1[113] & 9;
|
||||
printf("buffer2[33] = %02x\n", buffer2[33]);
|
||||
|
||||
buffer2[28] -= ((((2 | (buffer1[110] & 222)) >> 1) & ~223) | (buffer3[20] & 223));
|
||||
printf("buffer2[28] = %02x\n", buffer2[28]);
|
||||
|
||||
J = weird_rol8((V | Z), (U & 7)); // OK
|
||||
A = (buffer2[16] & T) | (W & (~buffer2[16]));
|
||||
B = (buffer1[33] & 17) | (X & ~17);
|
||||
E = ((Y | ((A+B) / 5)) & 147) |
|
||||
(Y & ((A+B) / 5)); // OK
|
||||
M = (buffer3[40] & buffer4[((buffer3[8] + J + E) & 0xff) % 21]) |
|
||||
((buffer3[40] | buffer4[((buffer3[8] + J + E) & 0xff) % 21]) & buffer2[23]);
|
||||
|
||||
buffer0[15] = (((buffer4[buffer3[20] % 21] - 48) & (~buffer1[184])) | ((buffer4[buffer3[20] % 21] - 48) & 189) | (189 & ~buffer1[184])) & (M*M*M);
|
||||
printf("buffer0[15] = %02x\n", buffer0[15]);
|
||||
|
||||
buffer2[22] += buffer1[183];
|
||||
printf("buffer2[22] = %02x\n", buffer2[22]);
|
||||
|
||||
buffer3[76] = (3 * buffer4[buffer1[1] % 21]) ^ buffer3[0];
|
||||
printf("buffer3[76] = %02x\n", buffer3[76]);
|
||||
|
||||
A = buffer2[((buffer3[8] + (J + E)) & 0xff) % 35];
|
||||
F = (((buffer4[buffer1[178] % 21] & A) | ((buffer4[buffer1[178] % 21] | A) & 209)) * buffer0[buffer1[13] % 20]) * (buffer4[buffer1[26] % 21] >> 1);
|
||||
G = (F + 0x733ffff9) * 198 - (((F + 0x733ffff9) * 396 + 212) & 212) + 85;
|
||||
buffer3[80] = buffer3[36] + (G ^ 148) + ((G ^ 107) << 1) - 127;
|
||||
printf("buffer3[80] = %02x\n", buffer3[80]);
|
||||
|
||||
buffer3[84] = ((buffer2[buffer3[64] % 35]) & 245) | (buffer2[buffer3[20] % 35] & 10);
|
||||
printf("buffer3[84] = %02x\n", buffer3[84]);
|
||||
|
||||
A = buffer0[buffer3[68] % 20] | 81;
|
||||
buffer2[18] -= ((A*A*A) & ~buffer0[15]) | ((buffer3[80] / 15) & buffer0[15]);
|
||||
printf("buffer2[18] = %02x\n", buffer2[18]);
|
||||
|
||||
buffer3[88] = buffer3[8] + J + E - buffer0[buffer1[160] % 20] + (buffer4[buffer0[((buffer3[8] + J + E) & 255) % 20] % 21] / 3);
|
||||
printf("buffer3[88] = %02x\n", buffer3[88]);
|
||||
|
||||
B = ((R ^ buffer3[72]) & ~198) | ((S * S) & 198);
|
||||
F = (buffer4[buffer1[69] % 21] & buffer1[172]) | ((buffer4[buffer1[69] % 21] | buffer1[172] ) & ((buffer3[12] - B) + 77));
|
||||
buffer0[16] = 147 - ((buffer3[72] & ((F & 251) | 1)) | (((F & 250) | buffer3[72]) & 198));
|
||||
printf("buffer0[16] = %02x\n", buffer0[16]);
|
||||
|
||||
C = (buffer4[buffer1[168] % 21] & buffer0[buffer1[29] % 20] & 7) | ((buffer4[buffer1[168] % 21] | buffer0[buffer1[29] % 20]) & 6);
|
||||
F = (buffer4[buffer1[155] % 21] & buffer1[105]) | ((buffer4[buffer1[155] % 21] | buffer1[105]) & 141);
|
||||
buffer0[3] -= buffer4[weird_rol32(F, C) % 21];
|
||||
printf("buffer0[3] = %02x\n", buffer0[3]);
|
||||
|
||||
buffer1[5] = weird_ror8(buffer0[12], ((buffer0[buffer1[61] % 20] / 5) & 7)) ^ (((~buffer2[buffer3[84] % 35]) & 0xffffffff) / 5);
|
||||
printf("buffer1[5] = %02x\n", buffer1[5]);
|
||||
|
||||
buffer1[198] += buffer1[3];
|
||||
printf("buffer1[198] = %02x\n", buffer1[198]);
|
||||
|
||||
A = (162 | buffer2[buffer3[64] % 35]);
|
||||
buffer1[164] += ((A*A)/5);
|
||||
printf("buffer1[164] = %02x\n", buffer1[164]);
|
||||
|
||||
G = weird_ror8(139, (buffer3[80] & 7));
|
||||
C = ((buffer4[buffer3[64] % 21] * buffer4[buffer3[64] % 21] * buffer4[buffer3[64] % 21]) & 95) | (buffer0[buffer3[40] % 20] & ~95);
|
||||
buffer3[92] = (G & 12) | (buffer0[buffer3[20] % 20] & 12) | (G & buffer0[buffer3[20] % 20]) | C;
|
||||
printf("buffer3[92] = %02x\n", buffer3[92]);
|
||||
|
||||
buffer2[12] += ((buffer1[103] & 32) | (buffer3[92] & ((buffer1[103] | 60))) | 16)/3;
|
||||
printf("buffer2[12] = %02x\n", buffer2[12]);
|
||||
|
||||
buffer3[96] = buffer1[143];
|
||||
printf("buffer3[96] = %02x\n", buffer3[96]);
|
||||
|
||||
buffer3[100] = 27;
|
||||
printf("buffer3[100] = %02x\n", buffer3[100]);
|
||||
|
||||
buffer3[104] = (((buffer3[40] & ~buffer2[8]) | (buffer1[35] & buffer2[8])) & buffer3[64]) ^ 119;
|
||||
printf("buffer3[104] = %02x\n", buffer3[104]);
|
||||
|
||||
buffer3[108] = 238 & ((((buffer3[40] & ~buffer2[8]) | (buffer1[35] & buffer2[8])) & buffer3[64]) << 1);
|
||||
printf("buffer3[108] = %02x\n", buffer3[108]);
|
||||
|
||||
buffer3[112] = (~buffer3[64] & (buffer3[84] / 3)) ^ 49;
|
||||
printf("buffer3[112] = %02x\n", buffer3[112]);
|
||||
|
||||
buffer3[116] = 98 & ((~buffer3[64] & (buffer3[84] / 3)) << 1);
|
||||
printf("buffer3[116] = %02x\n", buffer3[116]);
|
||||
|
||||
// finale
|
||||
A = (buffer1[35] & buffer2[8]) | (buffer3[40] & ~buffer2[8]);
|
||||
B = (A & buffer3[64]) | (((buffer3[84] / 3) & ~buffer3[64]));
|
||||
buffer1[143] = buffer3[96] - ((B & (86 + ((buffer1[172] & 64) >> 1))) | (((((buffer1[172] & 65) >> 1) ^ 86) | ((~buffer3[64] & (buffer3[84] / 3)) | (((buffer3[40] & ~buffer2[8]) | (buffer1[35] & buffer2[8])) & buffer3[64]))) & buffer3[100]));
|
||||
printf("buffer1[143] = %02x\n", buffer1[143]);
|
||||
|
||||
buffer2[29] = 162;
|
||||
printf("buffer2[29] = %02x\n", buffer2[29]);
|
||||
|
||||
A = ((((buffer4[buffer3[88] % 21]) & 160) | (buffer0[buffer1[125] % 20] & 95)) >> 1);
|
||||
B = buffer2[buffer1[149] % 35] ^ (buffer1[43] * buffer1[43]);
|
||||
buffer0[15] += (B&A) | ((A|B) & 115);
|
||||
printf("buffer0[15] = %02x\n", buffer0[15]);
|
||||
|
||||
buffer3[120] = buffer3[64] - buffer0[buffer3[40] % 20];
|
||||
printf("buffer3[120] = %02x\n", buffer3[120]);
|
||||
|
||||
buffer1[95] = buffer4[buffer3[20] % 21];
|
||||
printf("buffer1[95] = %02x\n", buffer1[95]);
|
||||
|
||||
A = weird_ror8(buffer2[buffer3[80] % 35], (buffer2[buffer1[17] % 35] * buffer2[buffer1[17] % 35] * buffer2[buffer1[17] % 35]) & 7);
|
||||
buffer0[7] -= (A*A);
|
||||
printf("buffer0[7] = %02x\n", buffer0[7]);
|
||||
|
||||
buffer2[8] = buffer2[8] - buffer1[184] + (buffer4[buffer1[202] % 21] * buffer4[buffer1[202] % 21] * buffer4[buffer1[202] % 21]);
|
||||
printf("buffer2[8] = %02x\n", buffer2[8]);
|
||||
|
||||
buffer0[16] = (buffer2[buffer1[102] % 35] << 1) & 132;
|
||||
printf("buffer0[16] = %02x\n", buffer0[16]);
|
||||
|
||||
buffer3[124] = (buffer4[buffer3[40] % 21] >> 1) ^ buffer3[68];
|
||||
printf("buffer3[124] = %02x\n", buffer3[124]);
|
||||
|
||||
buffer0[7] -= (buffer0[buffer1[191] % 20] - (((buffer4[buffer1[80] % 21] << 1) & ~177) | (buffer4[buffer4[buffer3[88] % 21] % 21] & 177)));
|
||||
printf("buffer0[7] = %02x\n", buffer0[7]);
|
||||
|
||||
buffer0[6] = buffer0[buffer1[119] % 20];
|
||||
printf("buffer0[6] = %02x\n", buffer0[6]);
|
||||
|
||||
A = (buffer4[buffer1[190] % 21] & ~209) | (buffer1[118] & 209);
|
||||
B = buffer0[buffer3[120] % 20] * buffer0[buffer3[120] % 20];
|
||||
buffer0[12] = (buffer0[buffer3[84] % 20] ^ (buffer2[buffer1[71] % 35] + buffer2[buffer1[15] % 35])) & ((A & B) | ((A | B) & 27));
|
||||
printf("buffer0[12] = %02x\n", buffer0[12]);
|
||||
|
||||
B = (buffer1[32] & buffer2[buffer3[88] % 35]) | ((buffer1[32] | buffer2[buffer3[88] % 35]) & 23);
|
||||
D = (((buffer4[buffer1[57] % 21] * 231) & 169) | (B & 86));
|
||||
F = (((buffer0[buffer1[82] % 20] & ~29) | (buffer4[buffer3[124] % 21] & 29)) & 190) | (buffer4[(D/5) % 21] & ~190);
|
||||
H = buffer0[buffer3[40] % 20] * buffer0[buffer3[40] % 20] * buffer0[buffer3[40] % 20];
|
||||
K = (H & buffer1[82]) | (H & 92) | (buffer1[82] & 92);
|
||||
buffer3[128] = ((F & K) | ((F | K) & 192)) ^ (D/5);
|
||||
printf("buffer3[128] = %02x\n", buffer3[128]);
|
||||
|
||||
buffer2[25] ^= ((buffer0[buffer3[120] % 20] << 1) * buffer1[5]) - (weird_rol8(buffer3[76], (buffer4[buffer3[124] % 21] & 7)) & (buffer3[20] + 110));
|
||||
printf("buffer2[25] = %02x\n", buffer2[25]);
|
||||
//exit(0);
|
||||
|
||||
}
|
||||
119
lib/playfair/modified_md5.c
Executable file
119
lib/playfair/modified_md5.c
Executable file
@@ -0,0 +1,119 @@
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#define printf(...) (void)0;
|
||||
|
||||
int shift[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
|
||||
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
|
||||
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
|
||||
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21};
|
||||
|
||||
uint32_t F(uint32_t B, uint32_t C, uint32_t D)
|
||||
{
|
||||
return (B & C) | (~B & D);
|
||||
}
|
||||
|
||||
uint32_t G(uint32_t B, uint32_t C, uint32_t D)
|
||||
{
|
||||
return (B & D) | (C & ~D);
|
||||
}
|
||||
|
||||
uint32_t H(uint32_t B, uint32_t C, uint32_t D)
|
||||
{
|
||||
return B ^ C ^ D;
|
||||
}
|
||||
|
||||
uint32_t I(uint32_t B, uint32_t C, uint32_t D)
|
||||
{
|
||||
return C ^ (B | ~D);
|
||||
}
|
||||
|
||||
|
||||
uint32_t rol(uint32_t input, int count)
|
||||
{
|
||||
return ((input << count) & 0xffffffff) | (input & 0xffffffff) >> (32-count);
|
||||
}
|
||||
|
||||
void swap(uint32_t* a, uint32_t* b)
|
||||
{
|
||||
printf("%08x <-> %08x\n", *a, *b);
|
||||
uint32_t c = *a;
|
||||
*a = *b;
|
||||
*b = c;
|
||||
}
|
||||
|
||||
void modified_md5(unsigned char* originalblockIn, unsigned char* keyIn, unsigned char* keyOut)
|
||||
{
|
||||
unsigned char blockIn[64];
|
||||
uint32_t* block_words = (uint32_t*)blockIn;
|
||||
uint32_t* key_words = (uint32_t*)keyIn;
|
||||
uint32_t* out_words = (uint32_t*)keyOut;
|
||||
uint32_t A, B, C, D, Z, tmp;
|
||||
int i;
|
||||
|
||||
memcpy(blockIn, originalblockIn, 64);
|
||||
|
||||
// Each cycle does something like this:
|
||||
A = key_words[0];
|
||||
B = key_words[1];
|
||||
C = key_words[2];
|
||||
D = key_words[3];
|
||||
for (i = 0; i < 64; i++)
|
||||
{
|
||||
uint32_t input;
|
||||
int j;
|
||||
if (i < 16)
|
||||
j = i;
|
||||
else if (i < 32)
|
||||
j = (5*i + 1) % 16;
|
||||
else if (i < 48)
|
||||
j = (3*i + 5) % 16;
|
||||
else if (i < 64)
|
||||
j = 7*i % 16;
|
||||
|
||||
input = blockIn[4*j] << 24 | blockIn[4*j+1] << 16 | blockIn[4*j+2] << 8 | blockIn[4*j+3];
|
||||
printf("Key = %08x\n", A);
|
||||
Z = A + input + (int)(long long)((1LL << 32) * fabs(sin(i + 1)));
|
||||
if (i < 16)
|
||||
Z = rol(Z + F(B,C,D), shift[i]);
|
||||
else if (i < 32)
|
||||
Z = rol(Z + G(B,C,D), shift[i]);
|
||||
else if (i < 48)
|
||||
Z = rol(Z + H(B,C,D), shift[i]);
|
||||
else if (i < 64)
|
||||
Z = rol(Z + I(B,C,D), shift[i]);
|
||||
if (i == 63)
|
||||
printf("Ror is %08x\n", Z);
|
||||
printf("Output of round %d: %08X + %08X = %08X (shift %d, constant %08X)\n", i, Z, B, Z+B, shift[i], (int)(long long)((1LL << 32) * fabs(sin(i + 1))));
|
||||
Z = Z + B;
|
||||
tmp = D;
|
||||
D = C;
|
||||
C = B;
|
||||
B = Z;
|
||||
A = tmp;
|
||||
if (i == 31)
|
||||
{
|
||||
// swapsies
|
||||
swap(&block_words[A & 15], &block_words[B & 15]);
|
||||
swap(&block_words[C & 15], &block_words[D & 15]);
|
||||
swap(&block_words[(A & (15<<4))>>4], &block_words[(B & (15<<4))>>4]);
|
||||
swap(&block_words[(A & (15<<8))>>8], &block_words[(B & (15<<8))>>8]);
|
||||
swap(&block_words[(A & (15<<12))>>12], &block_words[(B & (15<<12))>>12]);
|
||||
}
|
||||
}
|
||||
printf("%08X %08X %08X %08X\n", A, B, C, D);
|
||||
// Now we can actually compute the output
|
||||
printf("Out:\n");
|
||||
printf("%08x + %08x = %08x\n", key_words[0], A, key_words[0] + A);
|
||||
printf("%08x + %08x = %08x\n", key_words[1], B, key_words[1] + B);
|
||||
printf("%08x + %08x = %08x\n", key_words[2], C, key_words[2] + C);
|
||||
printf("%08x + %08x = %08x\n", key_words[3], D, key_words[3] + D);
|
||||
out_words[0] = key_words[0] + A;
|
||||
out_words[1] = key_words[1] + B;
|
||||
out_words[2] = key_words[2] + C;
|
||||
out_words[3] = key_words[3] + D;
|
||||
|
||||
}
|
||||
540
lib/playfair/omg_hax.c
Executable file
540
lib/playfair/omg_hax.c
Executable file
@@ -0,0 +1,540 @@
|
||||
void modified_md5(unsigned char* originalblockIn, unsigned char* keyIn, unsigned char* keyOut);
|
||||
void sap_hash(unsigned char* blockIn, unsigned char* keyOut);
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "omg_hax.h"
|
||||
|
||||
#define printf(...) (void)0;
|
||||
|
||||
void xor_blocks(unsigned char* a, unsigned char* b, unsigned char* out)
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
out[i] = a[i] ^ b[i];
|
||||
}
|
||||
|
||||
|
||||
void z_xor(unsigned char* in, unsigned char* out, int blocks)
|
||||
{
|
||||
for (int j = 0; j < blocks; j++)
|
||||
for (int i = 0; i < 16; i++)
|
||||
out[j*16+i] = in[j*16+i] ^ z_key[i];
|
||||
}
|
||||
|
||||
void x_xor(unsigned char* in, unsigned char* out, int blocks)
|
||||
{
|
||||
for (int j = 0; j < blocks; j++)
|
||||
for (int i = 0; i < 16; i++)
|
||||
out[j*16+i] = in[j*16+i] ^ x_key[i];
|
||||
}
|
||||
|
||||
|
||||
void t_xor(unsigned char* in, unsigned char* out)
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
out[i] = in[i] ^ t_key[i];
|
||||
}
|
||||
|
||||
unsigned char sap_iv[] = {0x2B,0x84,0xFB,0x79,0xDA,0x75,0xB9,0x04,0x6C,0x24,0x73,0xF7,0xD1,0xC4,0xAB,0x0E,0x2B,0x84,0xFB,0x79,0x75,0xB9,0x04,0x6C,0x24,0x73};
|
||||
|
||||
unsigned char sap_key_material[] = {0xA1, 0x1A, 0x4A, 0x83,
|
||||
0xF2, 0x7A, 0x75, 0xEE,
|
||||
0xA2, 0x1A, 0x7D, 0xB8,
|
||||
0x8D, 0x77, 0x92, 0xAB};
|
||||
|
||||
unsigned char index_mangle[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36, 0x6C};
|
||||
|
||||
unsigned char* table_index(int i)
|
||||
{
|
||||
return &table_s1[((31*i) % 0x28) << 8];
|
||||
}
|
||||
|
||||
unsigned char* message_table_index(int i)
|
||||
{
|
||||
return &table_s2[(97*i % 144) << 8];
|
||||
}
|
||||
|
||||
void print_block(char* msg, unsigned char* dword)
|
||||
{
|
||||
printf("%s", msg);
|
||||
for (int i = 0; i < 16; i++)
|
||||
printf("%02X ", dword[i]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void permute_block_1(unsigned char* block)
|
||||
{
|
||||
block[0] = table_s3[block[0]];
|
||||
block[4] = table_s3[0x400+block[4]];
|
||||
block[8] = table_s3[0x800+block[8]];
|
||||
block[12] = table_s3[0xc00+block[12]];
|
||||
|
||||
unsigned char tmp = block[13];
|
||||
block[13] = table_s3[0x100+block[9]];
|
||||
block[9] = table_s3[0xd00+block[5]];
|
||||
block[5] = table_s3[0x900+block[1]];
|
||||
block[1] = table_s3[0x500+tmp];
|
||||
|
||||
tmp = block[2];
|
||||
block[2] = table_s3[0xa00+block[10]];
|
||||
block[10] = table_s3[0x200+tmp];
|
||||
tmp = block[6];
|
||||
block[6] = table_s3[0xe00+block[14]];
|
||||
block[14] = table_s3[0x600+tmp];
|
||||
|
||||
tmp = block[3];
|
||||
block[3] = table_s3[0xf00+block[7]];
|
||||
block[7] = table_s3[0x300+block[11]];
|
||||
block[11] = table_s3[0x700+block[15]];
|
||||
block[15] = table_s3[0xb00+tmp];
|
||||
print_block("Permutation complete. Final value of block: ", block); // This looks right to me, at least for decrypt_kernel
|
||||
}
|
||||
|
||||
unsigned char* permute_table_2(unsigned int i)
|
||||
{
|
||||
return &table_s4[((71 * i) % 144) << 8];
|
||||
}
|
||||
|
||||
void permute_block_2(unsigned char* block, int round)
|
||||
{
|
||||
// round is 0..8?
|
||||
printf("Permuting via table2, round %d... (block[0] = %02X)\n", round, block[0]);
|
||||
block[0] = permute_table_2(round*16+0)[block[0]];
|
||||
block[4] = permute_table_2(round*16+4)[block[4]];
|
||||
block[8] = permute_table_2(round*16+8)[block[8]];
|
||||
block[12] = permute_table_2(round*16+12)[block[12]];
|
||||
|
||||
unsigned char tmp = block[13];
|
||||
block[13] = permute_table_2(round*16+13)[block[9]];
|
||||
block[9] = permute_table_2(round*16+9)[block[5]];
|
||||
block[5] = permute_table_2(round*16+5)[block[1]];
|
||||
block[1] = permute_table_2(round*16+1)[tmp];
|
||||
|
||||
tmp = block[2];
|
||||
block[2] = permute_table_2(round*16+2)[block[10]];
|
||||
block[10] = permute_table_2(round*16+10)[tmp];
|
||||
tmp = block[6];
|
||||
block[6] = permute_table_2(round*16+6)[block[14]];
|
||||
block[14] = permute_table_2(round*16+14)[tmp];
|
||||
|
||||
tmp = block[3];
|
||||
block[3] = permute_table_2(round*16+3)[block[7]];
|
||||
block[7] = permute_table_2(round*16+7)[block[11]];
|
||||
block[11] = permute_table_2(round*16+11)[block[15]];
|
||||
block[15] = permute_table_2(round*16+15)[tmp];
|
||||
print_block("Permutation (2) complete. Final value of block: ", block); // This looks right to me, at least for decrypt_kernel
|
||||
}
|
||||
|
||||
// This COULD just be Rijndael key expansion, but with a different set of S-boxes
|
||||
void generate_key_schedule(unsigned char* key_material, uint32_t key_schedule[11][4])
|
||||
{
|
||||
uint32_t key_data[4];
|
||||
int i;
|
||||
for (i = 0; i < 11; i++)
|
||||
{
|
||||
key_schedule[i][0] = 0xdeadbeef;
|
||||
key_schedule[i][1] = 0xdeadbeef;
|
||||
key_schedule[i][2] = 0xdeadbeef;
|
||||
key_schedule[i][3] = 0xdeadbeef;
|
||||
}
|
||||
unsigned char* buffer = (unsigned char*)key_data;
|
||||
int ti = 0;
|
||||
printf("Generating key schedule\n");
|
||||
// G
|
||||
print_block("Raw key material: ", key_material);
|
||||
t_xor(key_material, buffer);
|
||||
print_block("G has produced: ", buffer);
|
||||
for (int round = 0; round < 11; round++)
|
||||
{
|
||||
printf("Starting round %d\n", round);
|
||||
// H
|
||||
key_schedule[round][0] = key_data[0];
|
||||
printf("H has set chunk 1 of round %d %08X\n", round, key_schedule[round][0]);
|
||||
printf("H complete\n");
|
||||
// I
|
||||
unsigned char* table1 = table_index(ti);
|
||||
unsigned char* table2 = table_index(ti+1);
|
||||
unsigned char* table3 = table_index(ti+2);
|
||||
unsigned char* table4 = table_index(ti+3);
|
||||
ti += 4;
|
||||
//buffer[0] = (buffer[0] - (4 & (buffer[0] << 1)) + 2) ^ 2 ^ index_mangle[round] ^ table1[buffer[0x0d]];
|
||||
printf("S-box: 0x%02x -> 0x%02x\n", buffer[0x0d], table1[buffer[0x0d]]);
|
||||
printf("S-box: 0x%02x -> 0x%02x\n", buffer[0x0e], table2[buffer[0x0e]]);
|
||||
printf("S-box: 0x%02x -> 0x%02x\n", buffer[0x0f], table3[buffer[0x0f]]);
|
||||
printf("S-box: 0x%02x -> 0x%02x\n", buffer[0x0c], table4[buffer[0x0c]]);
|
||||
buffer[0] ^= table1[buffer[0x0d]] ^ index_mangle[round];
|
||||
buffer[1] ^= table2[buffer[0x0e]];
|
||||
buffer[2] ^= table3[buffer[0x0f]];
|
||||
buffer[3] ^= table4[buffer[0x0c]];
|
||||
print_block("After I, buffer is now: ", buffer);
|
||||
printf("I complete\n");
|
||||
// H
|
||||
key_schedule[round][1] = key_data[1];
|
||||
printf("H has set chunk 2 to %08X\n", key_schedule[round][1]);
|
||||
|
||||
printf("H complete\n");
|
||||
// J
|
||||
key_data[1] ^= key_data[0];
|
||||
printf("J complete\n");
|
||||
print_block("Buffer is now ", buffer);
|
||||
// H
|
||||
key_schedule[round][2] = key_data[2];
|
||||
printf("H has set chunk3 to %08X\n", key_schedule[round][2]);
|
||||
printf("H complete\n");
|
||||
|
||||
// J
|
||||
key_data[2] ^= key_data[1];
|
||||
printf("J complete\n");
|
||||
// K and L
|
||||
// Implement K and L to fill in other bits of the key schedule
|
||||
key_schedule[round][3] = key_data[3];
|
||||
// J again
|
||||
key_data[3] ^= key_data[2];
|
||||
printf("J complete\n");
|
||||
}
|
||||
for (i = 0; i < 11; i++)
|
||||
print_block("Schedule: ", (unsigned char*)key_schedule[i]);
|
||||
}
|
||||
|
||||
// This MIGHT just be AES, or some variant thereof.
|
||||
void cycle(unsigned char* block, uint32_t key_schedule[11][4])
|
||||
{
|
||||
uint32_t ptr1 = 0;
|
||||
uint32_t ptr2 = 0;
|
||||
uint32_t ptr3 = 0;
|
||||
uint32_t ptr4 = 0;
|
||||
uint32_t ab;
|
||||
unsigned char* buffer = (unsigned char*)&ab;
|
||||
uint32_t* bWords = (uint32_t*)block;
|
||||
bWords[0] ^= key_schedule[10][0];
|
||||
bWords[1] ^= key_schedule[10][1];
|
||||
bWords[2] ^= key_schedule[10][2];
|
||||
bWords[3] ^= key_schedule[10][3];
|
||||
// First, these are permuted
|
||||
permute_block_1(block);
|
||||
|
||||
for (int round = 0; round < 9; round++)
|
||||
{
|
||||
// E
|
||||
// Note that table_s5 is a table of 4-byte words. Therefore we do not need to <<2 these indices
|
||||
// TODO: Are these just T-tables?
|
||||
unsigned char* key0 = (unsigned char*)&key_schedule[9-round][0];
|
||||
ptr1 = table_s5[block[3] ^ key0[3]];
|
||||
ptr2 = table_s6[block[2] ^ key0[2]];
|
||||
ptr3 = table_s8[block[0] ^ key0[0]];
|
||||
ptr4 = table_s7[block[1] ^ key0[1]];
|
||||
|
||||
// A B
|
||||
ab = ptr1 ^ ptr2 ^ ptr3 ^ ptr4;
|
||||
printf("ab: %08X %08X %08X %08X -> %08X\n", ptr1, ptr2, ptr3, ptr4, ab);
|
||||
// C
|
||||
((uint32_t*)block)[0] = ab;
|
||||
printf("f7 = %02X\n", block[7]);
|
||||
unsigned char* key1 = (unsigned char*)&key_schedule[9-round][1];
|
||||
ptr2 = table_s5[block[7] ^ key1[3]];
|
||||
ptr1 = table_s6[block[6] ^ key1[2]];
|
||||
ptr4 = table_s7[block[5] ^ key1[1]];
|
||||
ptr3 = table_s8[block[4] ^ key1[0]];
|
||||
// A B again
|
||||
ab = ptr1 ^ ptr2 ^ ptr3 ^ ptr4;
|
||||
printf("ab: %08X %08X %08X %08X -> %08X\n", ptr1, ptr2, ptr3, ptr4, ab);
|
||||
// D is a bit of a nightmare, but it is really not as complicated as you might think
|
||||
unsigned char* key2 = (unsigned char*)&key_schedule[9-round][2];
|
||||
unsigned char* key3 = (unsigned char*)&key_schedule[9-round][3];
|
||||
((uint32_t*)block)[1] = ab;
|
||||
((uint32_t*)block)[2] = table_s5[block[11] ^ key2[3]] ^
|
||||
table_s6[block[10] ^ key2[2]] ^
|
||||
table_s7[block[9] ^ key2[1]] ^
|
||||
table_s8[block[8] ^ key2[0]];
|
||||
|
||||
((uint32_t*)block)[3] = table_s5[block[15] ^ key3[3]] ^
|
||||
table_s6[block[14] ^ key3[2]] ^
|
||||
table_s7[block[13] ^ key3[1]] ^
|
||||
table_s8[block[12] ^ key3[0]];
|
||||
printf("Set block2 = %08X, block3 = %08X\n", ((uint32_t*)block)[2], ((uint32_t*)block)[3]);
|
||||
// In the last round, instead of the permute, we do F
|
||||
permute_block_2(block, 8-round);
|
||||
}
|
||||
printf("Using last bit of key up: %08X xor %08X -> %08X\n", ((uint32_t*)block)[0], key_schedule[0][0], ((uint32_t*)block)[0] ^ key_schedule[0][0]);
|
||||
((uint32_t*)block)[0] ^= key_schedule[0][0];
|
||||
((uint32_t*)block)[1] ^= key_schedule[0][1];
|
||||
((uint32_t*)block)[2] ^= key_schedule[0][2];
|
||||
((uint32_t*)block)[3] ^= key_schedule[0][3];
|
||||
}
|
||||
|
||||
|
||||
void decrypt_sap(unsigned char* sapIn, unsigned char* sapOut)
|
||||
{
|
||||
uint32_t key_schedule[11][4];
|
||||
unsigned char* iv;
|
||||
print_block("Base sap: ", &sapIn[0xf0]);
|
||||
z_xor(sapIn, sapOut, 16);
|
||||
generate_key_schedule(sap_key_material, key_schedule);
|
||||
print_block("lastSap before cycle: ", &sapOut[0xf0]);
|
||||
for (int i = 0xf0; i >= 0x00; i-=0x10)
|
||||
{
|
||||
printf("Ready to cycle %02X\n", i);
|
||||
cycle(&sapOut[i], key_schedule);
|
||||
print_block("After cycling, block is: ", &sapOut[i]);
|
||||
if (i > 0)
|
||||
{ // xor with previous block
|
||||
iv = &sapOut[i-0x10];
|
||||
}
|
||||
else
|
||||
{ // xor with sap IV
|
||||
iv = sap_iv;
|
||||
}
|
||||
for (int j = 0; j < 16; j++)
|
||||
{
|
||||
printf("%02X ^ %02X -> %02X\n", sapOut[i+j], iv[j], sapOut[i+j] ^ iv[j]);
|
||||
sapOut[i+j] = sapOut[i+j] ^ iv[j];
|
||||
}
|
||||
printf("Decrypted SAP %02X-%02X:\n", i, i+0xf);
|
||||
print_block("", &sapOut[i]);
|
||||
}
|
||||
// Lastly grind the whole thing through x_key. This is the last time we modify sap
|
||||
x_xor(sapOut, sapOut, 16);
|
||||
printf("Sap is decrypted to\n");
|
||||
for (int i = 0xf0; i >= 0x00; i-=0x10)
|
||||
{
|
||||
printf("Final SAP %02X-%02X: ", i, i+0xf);
|
||||
print_block("", &sapOut[i]);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char initial_session_key[] = {0xDC, 0xDC, 0xF3, 0xB9, 0x0B, 0x74, 0xDC, 0xFB, 0x86, 0x7F, 0xF7, 0x60, 0x16, 0x72, 0x90, 0x51};
|
||||
|
||||
|
||||
void decrypt_key(unsigned char* decryptedSap, unsigned char* keyIn, unsigned char* iv, unsigned char* keyOut)
|
||||
{
|
||||
unsigned char blockIn[16];
|
||||
uint32_t key_schedule[11][4];
|
||||
uint32_t mode_key_schedule[11][4];
|
||||
generate_key_schedule(&decryptedSap[8], key_schedule);
|
||||
printf("Generating mode key:\n");
|
||||
generate_key_schedule(initial_session_key, mode_key_schedule);
|
||||
z_xor(keyIn, blockIn, 1);
|
||||
print_block("Input to cycle is: ", blockIn);
|
||||
cycle(blockIn, key_schedule);
|
||||
for (int j = 0; j < 16; j++)
|
||||
keyOut[j] = blockIn[j] ^ iv[j];
|
||||
print_block("Output from cycle is: ", keyOut);
|
||||
x_xor(keyOut, keyOut, 1);
|
||||
}
|
||||
|
||||
|
||||
void decryptMessage(unsigned char* messageIn, unsigned char* decryptedMessage)
|
||||
{
|
||||
unsigned char buffer[16];
|
||||
int i, j;
|
||||
unsigned char tmp;
|
||||
uint32_t key_schedule[11][4];
|
||||
int mode = messageIn[12]; // 0,1,2,3
|
||||
printf("mode = %02x\n", mode);
|
||||
generate_key_schedule(initial_session_key, key_schedule);
|
||||
|
||||
// For M0-M6 we follow the same pattern
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
// First, copy in the nth block (we must start with the last one)
|
||||
for (j = 0; j < 16; j++)
|
||||
{
|
||||
if (mode == 3)
|
||||
buffer[j] = messageIn[(0x80-0x10*i)+j];
|
||||
else if (mode == 2 || mode == 1 || mode == 0)
|
||||
buffer[j] = messageIn[(0x10*(i+1))+j];
|
||||
}
|
||||
// do this permutation and update 9 times. Could this be cycle(), or the reverse of cycle()?
|
||||
for (j = 0; j < 9; j++)
|
||||
{
|
||||
int base = 0x80 - 0x10*j;
|
||||
//print_block("About to cycle. Buffer is currently: ", buffer);
|
||||
buffer[0x0] = message_table_index(base+0x0)[buffer[0x0]] ^ message_key[mode][base+0x0];
|
||||
buffer[0x4] = message_table_index(base+0x4)[buffer[0x4]] ^ message_key[mode][base+0x4];
|
||||
buffer[0x8] = message_table_index(base+0x8)[buffer[0x8]] ^ message_key[mode][base+0x8];
|
||||
buffer[0xc] = message_table_index(base+0xc)[buffer[0xc]] ^ message_key[mode][base+0xc];
|
||||
|
||||
tmp = buffer[0x0d];
|
||||
buffer[0xd] = message_table_index(base+0xd)[buffer[0x9]] ^ message_key[mode][base+0xd];
|
||||
buffer[0x9] = message_table_index(base+0x9)[buffer[0x5]] ^ message_key[mode][base+0x9];
|
||||
buffer[0x5] = message_table_index(base+0x5)[buffer[0x1]] ^ message_key[mode][base+0x5];
|
||||
buffer[0x1] = message_table_index(base+0x1)[tmp] ^ message_key[mode][base+0x1];
|
||||
|
||||
tmp = buffer[0x02];
|
||||
buffer[0x2] = message_table_index(base+0x2)[buffer[0xa]] ^ message_key[mode][base+0x2];
|
||||
buffer[0xa] = message_table_index(base+0xa)[tmp] ^ message_key[mode][base+0xa];
|
||||
tmp = buffer[0x06];
|
||||
buffer[0x6] = message_table_index(base+0x6)[buffer[0xe]] ^ message_key[mode][base+0x6];
|
||||
buffer[0xe] = message_table_index(base+0xe)[tmp] ^ message_key[mode][base+0xe];
|
||||
|
||||
tmp = buffer[0x3];
|
||||
buffer[0x3] = message_table_index(base+0x3)[buffer[0x7]] ^ message_key[mode][base+0x3];
|
||||
buffer[0x7] = message_table_index(base+0x7)[buffer[0xb]] ^ message_key[mode][base+0x7];
|
||||
buffer[0xb] = message_table_index(base+0xb)[buffer[0xf]] ^ message_key[mode][base+0xb];
|
||||
buffer[0xf] = message_table_index(base+0xf)[tmp] ^ message_key[mode][base+0xf];
|
||||
|
||||
// Now we must replace the entire buffer with 4 words that we read and xor together
|
||||
uint32_t word;
|
||||
uint32_t* block = (uint32_t*)buffer;
|
||||
|
||||
block[0] = table_s9[0x000 + buffer[0x0]] ^
|
||||
table_s9[0x100 + buffer[0x1]] ^
|
||||
table_s9[0x200 + buffer[0x2]] ^
|
||||
table_s9[0x300 + buffer[0x3]];
|
||||
block[1] = table_s9[0x000 + buffer[0x4]] ^
|
||||
table_s9[0x100 + buffer[0x5]] ^
|
||||
table_s9[0x200 + buffer[0x6]] ^
|
||||
table_s9[0x300 + buffer[0x7]];
|
||||
block[2] = table_s9[0x000 + buffer[0x8]] ^
|
||||
table_s9[0x100 + buffer[0x9]] ^
|
||||
table_s9[0x200 + buffer[0xa]] ^
|
||||
table_s9[0x300 + buffer[0xb]];
|
||||
block[3] = table_s9[0x000 + buffer[0xc]] ^
|
||||
table_s9[0x100 + buffer[0xd]] ^
|
||||
table_s9[0x200 + buffer[0xe]] ^
|
||||
table_s9[0x300 + buffer[0xf]];
|
||||
}
|
||||
// Next, another permute with a different table
|
||||
buffer[0x0] = table_s10[(0x0 << 8) + buffer[0x0]];
|
||||
buffer[0x4] = table_s10[(0x4 << 8) + buffer[0x4]];
|
||||
buffer[0x8] = table_s10[(0x8 << 8) + buffer[0x8]];
|
||||
buffer[0xc] = table_s10[(0xc << 8) + buffer[0xc]];
|
||||
|
||||
tmp = buffer[0x0d];
|
||||
buffer[0xd] = table_s10[(0xd << 8) + buffer[0x9]];
|
||||
buffer[0x9] = table_s10[(0x9 << 8) + buffer[0x5]];
|
||||
buffer[0x5] = table_s10[(0x5 << 8) + buffer[0x1]];
|
||||
buffer[0x1] = table_s10[(0x1 << 8) + tmp];
|
||||
|
||||
tmp = buffer[0x02];
|
||||
buffer[0x2] = table_s10[(0x2 << 8) + buffer[0xa]];
|
||||
buffer[0xa] = table_s10[(0xa << 8) + tmp];
|
||||
tmp = buffer[0x06];
|
||||
buffer[0x6] = table_s10[(0x6 << 8) + buffer[0xe]];
|
||||
buffer[0xe] = table_s10[(0xe << 8) + tmp];
|
||||
|
||||
tmp = buffer[0x3];
|
||||
buffer[0x3] = table_s10[(0x3 << 8) + buffer[0x7]];
|
||||
buffer[0x7] = table_s10[(0x7 << 8) + buffer[0xb]];
|
||||
buffer[0xb] = table_s10[(0xb << 8) + buffer[0xf]];
|
||||
buffer[0xf] = table_s10[(0xf << 8) + tmp];
|
||||
|
||||
// And finally xor with the previous block of the message, except in mode-2 where we do this in reverse
|
||||
if (mode == 2 || mode == 1 || mode == 0)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
xor_blocks(buffer, &messageIn[0x10*i], &decryptedMessage[0x10*i]); // remember that the first 0x10 bytes are the header
|
||||
}
|
||||
else
|
||||
xor_blocks(buffer, message_iv[mode], &decryptedMessage[0x10*i]);
|
||||
print_block(" ", &decryptedMessage[0x10*i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i < 7)
|
||||
xor_blocks(buffer, &messageIn[0x70 - 0x10*i], &decryptedMessage[0x70 - 0x10*i]);
|
||||
else
|
||||
xor_blocks(buffer, message_iv[mode], &decryptedMessage[0x70 - 0x10*i]);
|
||||
printf("Decrypted message block %02X-%02X:", 0x70 - 0x10*i, 0x70 - 0x10*i+0xf);
|
||||
print_block(" ", &decryptedMessage[0x70 - 0x10*i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char static_source_1[] = {0xFA, 0x9C, 0xAD, 0x4D, 0x4B, 0x68, 0x26, 0x8C, 0x7F, 0xF3, 0x88, 0x99, 0xDE, 0x92, 0x2E, 0x95,
|
||||
0x1E};
|
||||
unsigned char static_source_2[] = {0xEC, 0x4E, 0x27, 0x5E, 0xFD, 0xF2, 0xE8, 0x30, 0x97, 0xAE, 0x70, 0xFB, 0xE0, 0x00, 0x3F, 0x1C,
|
||||
0x39, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x09, 0x00, 0x0, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
void swap_bytes(unsigned char* a, unsigned char *b)
|
||||
{
|
||||
unsigned char c = *a;
|
||||
*a = *b;
|
||||
*b = c;
|
||||
}
|
||||
|
||||
void generate_session_key(unsigned char* oldSap, unsigned char* messageIn, unsigned char* sessionKey)
|
||||
{
|
||||
unsigned char decryptedMessage[128];
|
||||
unsigned char newSap[320];
|
||||
unsigned char Q[210];
|
||||
int i;
|
||||
int round;
|
||||
unsigned char md5[16];
|
||||
unsigned char otherHash[16];
|
||||
|
||||
decryptMessage(messageIn, decryptedMessage);
|
||||
// Now that we have the decrypted message, we can combine it with our initial sap to form the 5 blocks needed to generate the 5 words which, when added together, give
|
||||
// the session key.
|
||||
memcpy(&newSap[0x000], static_source_1, 0x11);
|
||||
memcpy(&newSap[0x011], decryptedMessage, 0x80);
|
||||
memcpy(&newSap[0x091], &oldSap[0x80], 0x80);
|
||||
memcpy(&newSap[0x111], static_source_2, 0x2f);
|
||||
memcpy(sessionKey, initial_session_key, 16);
|
||||
|
||||
for (round = 0; round < 5; round++)
|
||||
{
|
||||
unsigned char* base = &newSap[round * 64];
|
||||
print_block("Input block: ", &base[0]);
|
||||
print_block("Input block: ", &base[0x10]);
|
||||
print_block("Input block: ", &base[0x20]);
|
||||
print_block("Input block: ", &base[0x30]);
|
||||
modified_md5(base, sessionKey, md5);
|
||||
printf("MD5 OK\n");
|
||||
sap_hash(base, sessionKey);
|
||||
printf("OtherHash OK\n");
|
||||
|
||||
printf("MD5 = ");
|
||||
for (i = 0; i < 4; i++)
|
||||
printf("%08x ", ((uint32_t*)md5)[i]);
|
||||
printf("\nOtherHash = ");
|
||||
for (i = 0; i < 4; i++)
|
||||
printf("%08x ", ((uint32_t*)sessionKey)[i]);
|
||||
printf("\n");
|
||||
|
||||
uint32_t* sessionKeyWords = (uint32_t*)sessionKey;
|
||||
uint32_t* md5Words = (uint32_t*)md5;
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
sessionKeyWords[i] = (sessionKeyWords[i] + md5Words[i]) & 0xffffffff;
|
||||
}
|
||||
printf("Current key: ");
|
||||
for (i = 0; i < 16; i++)
|
||||
printf("%02x", sessionKey[i]);
|
||||
printf("\n");
|
||||
}
|
||||
for (i = 0; i < 16; i+=4)
|
||||
{
|
||||
swap_bytes(&sessionKey[i], &sessionKey[i+3]);
|
||||
swap_bytes(&sessionKey[i+1], &sessionKey[i+2]);
|
||||
}
|
||||
|
||||
// Finally the whole thing is XORd with 121:
|
||||
for (i = 0; i < 16; i++)
|
||||
sessionKey[i] ^= 121;
|
||||
print_block("Session key computed as: ", sessionKey);
|
||||
}
|
||||
|
||||
unsigned char default_sap[] =
|
||||
{ 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79,
|
||||
0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79,
|
||||
0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79,
|
||||
0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79,
|
||||
0x79, 0x79, 0x79, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x02, 0x53,
|
||||
0x00, 0x01, 0xcc, 0x34, 0x2a, 0x5e, 0x5b, 0x1a, 0x67, 0x73, 0xc2, 0x0e, 0x21, 0xb8, 0x22, 0x4d,
|
||||
0xf8, 0x62, 0x48, 0x18, 0x64, 0xef, 0x81, 0x0a, 0xae, 0x2e, 0x37, 0x03, 0xc8, 0x81, 0x9c, 0x23,
|
||||
0x53, 0x9d, 0xe5, 0xf5, 0xd7, 0x49, 0xbc, 0x5b, 0x7a, 0x26, 0x6c, 0x49, 0x62, 0x83, 0xce, 0x7f,
|
||||
0x03, 0x93, 0x7a, 0xe1, 0xf6, 0x16, 0xde, 0x0c, 0x15, 0xff, 0x33, 0x8c, 0xca, 0xff, 0xb0, 0x9e,
|
||||
0xaa, 0xbb, 0xe4, 0x0f, 0x5d, 0x5f, 0x55, 0x8f, 0xb9, 0x7f, 0x17, 0x31, 0xf8, 0xf7, 0xda, 0x60,
|
||||
0xa0, 0xec, 0x65, 0x79, 0xc3, 0x3e, 0xa9, 0x83, 0x12, 0xc3, 0xb6, 0x71, 0x35, 0xa6, 0x69, 0x4f,
|
||||
0xf8, 0x23, 0x05, 0xd9, 0xba, 0x5c, 0x61, 0x5f, 0xa2, 0x54, 0xd2, 0xb1, 0x83, 0x45, 0x83, 0xce,
|
||||
0xe4, 0x2d, 0x44, 0x26, 0xc8, 0x35, 0xa7, 0xa5, 0xf6, 0xc8, 0x42, 0x1c, 0x0d, 0xa3, 0xf1, 0xc7,
|
||||
0x00, 0x50, 0xf2, 0xe5, 0x17, 0xf8, 0xd0, 0xfa, 0x77, 0x8d, 0xfb, 0x82, 0x8d, 0x40, 0xc7, 0x8e,
|
||||
0x94, 0x1e, 0x1e, 0x1e};
|
||||
32
lib/playfair/omg_hax.h
Executable file
32
lib/playfair/omg_hax.h
Executable file
File diff suppressed because one or more lines are too long
31
lib/playfair/playfair.c
Executable file
31
lib/playfair/playfair.c
Executable file
@@ -0,0 +1,31 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "playfair.h"
|
||||
|
||||
void generate_key_schedule(unsigned char* key_material, uint32_t key_schedule[11][4]);
|
||||
void generate_session_key(unsigned char* oldSap, unsigned char* messageIn, unsigned char* sessionKey);
|
||||
void cycle(unsigned char* block, uint32_t key_schedule[11][4]);
|
||||
void z_xor(unsigned char* in, unsigned char* out, int blocks);
|
||||
void x_xor(unsigned char* in, unsigned char* out, int blocks);
|
||||
|
||||
extern unsigned char default_sap[];
|
||||
|
||||
void playfair_decrypt(unsigned char* message3, unsigned char* cipherText, unsigned char* keyOut)
|
||||
{
|
||||
unsigned char* chunk1 = &cipherText[16];
|
||||
unsigned char* chunk2 = &cipherText[56];
|
||||
int i;
|
||||
unsigned char blockIn[16];
|
||||
unsigned char sapKey[16];
|
||||
uint32_t key_schedule[11][4];
|
||||
generate_session_key(default_sap, message3, sapKey);
|
||||
generate_key_schedule(sapKey, key_schedule);
|
||||
z_xor(chunk2, blockIn, 1);
|
||||
cycle(blockIn, key_schedule);
|
||||
for (i = 0; i < 16; i++) {
|
||||
keyOut[i] = blockIn[i] ^ chunk1[i];
|
||||
}
|
||||
x_xor(keyOut, keyOut, 1);
|
||||
z_xor(keyOut, keyOut, 1);
|
||||
}
|
||||
|
||||
6
lib/playfair/playfair.h
Executable file
6
lib/playfair/playfair.h
Executable file
@@ -0,0 +1,6 @@
|
||||
#ifndef PLAYFAIR_H
|
||||
#define PLAYFAIR_H
|
||||
|
||||
void playfair_decrypt(unsigned char* message3, unsigned char* cipherText, unsigned char* keyOut);
|
||||
|
||||
#endif
|
||||
96
lib/playfair/sap_hash.c
Executable file
96
lib/playfair/sap_hash.c
Executable file
@@ -0,0 +1,96 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define printf(...) (void)0;
|
||||
|
||||
void garble(unsigned char*, unsigned char*, unsigned char*, unsigned char*, unsigned char*);
|
||||
|
||||
unsigned char rol8(unsigned char input, int count)
|
||||
{
|
||||
return ((input << count) & 0xff) | (input & 0xff) >> (8-count);
|
||||
}
|
||||
|
||||
uint32_t rol8x(unsigned char input, int count)
|
||||
{
|
||||
return ((input << count)) | (input) >> (8-count);
|
||||
}
|
||||
|
||||
|
||||
void sap_hash(unsigned char* blockIn, unsigned char* keyOut)
|
||||
{
|
||||
uint32_t* block_words = (uint32_t*)blockIn;
|
||||
uint32_t* out_words = (uint32_t*)keyOut;
|
||||
unsigned char buffer0[20] = {0x96, 0x5F, 0xC6, 0x53, 0xF8, 0x46, 0xCC, 0x18, 0xDF, 0xBE, 0xB2, 0xF8, 0x38, 0xD7, 0xEC, 0x22, 0x03, 0xD1, 0x20, 0x8F};
|
||||
unsigned char buffer1[210];
|
||||
unsigned char buffer2[35] = {0x43, 0x54, 0x62, 0x7A, 0x18, 0xC3, 0xD6, 0xB3, 0x9A, 0x56, 0xF6, 0x1C, 0x14, 0x3F, 0x0C, 0x1D, 0x3B, 0x36, 0x83, 0xB1, 0x39, 0x51, 0x4A, 0xAA, 0x09, 0x3E, 0xFE, 0x44, 0xAF, 0xDE, 0xC3, 0x20, 0x9D, 0x42, 0x3A};
|
||||
unsigned char buffer3[132];
|
||||
unsigned char buffer4[21] = {0xED, 0x25, 0xD1, 0xBB, 0xBC, 0x27, 0x9F, 0x02, 0xA2, 0xA9, 0x11, 0x00, 0x0C, 0xB3, 0x52, 0xC0, 0xBD, 0xE3, 0x1B, 0x49, 0xC7};
|
||||
int i0_index[11] = {18, 22, 23, 0, 5, 19, 32, 31, 10, 21, 30};
|
||||
uint8_t w,x,y,z;
|
||||
int i, j;
|
||||
|
||||
// Load the input into the buffer
|
||||
for (i = 0; i < 210; i++)
|
||||
{
|
||||
// We need to swap the byte order around so it is the right endianness
|
||||
uint32_t in_word = block_words[((i % 64)>>2)];
|
||||
uint32_t in_byte = (in_word >> ((3-(i % 4)) << 3)) & 0xff;
|
||||
buffer1[i] = in_byte;
|
||||
}
|
||||
// Next a scrambling
|
||||
for (i = 0; i < 840; i++)
|
||||
{
|
||||
// We have to do unsigned, 32-bit modulo, or we get the wrong indices
|
||||
x = buffer1[((i-155) & 0xffffffff) % 210];
|
||||
y = buffer1[((i-57) & 0xffffffff) % 210];
|
||||
z = buffer1[((i-13) & 0xffffffff) % 210];
|
||||
w = buffer1[(i & 0xffffffff) % 210];
|
||||
buffer1[i % 210] = (rol8(y, 5) + (rol8(z, 3) ^ w) - rol8(x,7)) & 0xff;
|
||||
}
|
||||
printf("Garbling...\n");
|
||||
// I have no idea what this is doing (yet), but it gives the right output
|
||||
garble(buffer0, buffer1, buffer2, buffer3, buffer4);
|
||||
|
||||
// Fill the output with 0xE1
|
||||
for (i = 0; i < 16; i++)
|
||||
keyOut[i] = 0xE1;
|
||||
|
||||
// Now we use all the buffers we have calculated to grind out the output. First buffer3
|
||||
for (i = 0; i < 11; i++)
|
||||
{
|
||||
// Note that this is addition (mod 255) and not XOR
|
||||
// Also note that we only use certain indices
|
||||
// And that index 3 is hard-coded to be 0x3d (Maybe we can hack this up by changing buffer3[0] to be 0xdc?
|
||||
if (i == 3)
|
||||
keyOut[i] = 0x3d;
|
||||
else
|
||||
keyOut[i] = ((keyOut[i] + buffer3[i0_index[i] * 4]) & 0xff);
|
||||
}
|
||||
|
||||
// Then buffer0
|
||||
for (i = 0; i < 20; i++)
|
||||
keyOut[i % 16] ^= buffer0[i];
|
||||
|
||||
// Then buffer2
|
||||
for (i = 0; i < 35; i++)
|
||||
keyOut[i % 16] ^= buffer2[i];
|
||||
|
||||
// Do buffer1
|
||||
for (i = 0; i < 210; i++)
|
||||
keyOut[(i % 16)] ^= buffer1[i];
|
||||
|
||||
|
||||
// Now we do a kind of reverse-scramble
|
||||
for (j = 0; j < 16; j++)
|
||||
{
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
x = keyOut[((i-7) & 0xffffffff) % 16];
|
||||
y = keyOut[i % 16];
|
||||
z = keyOut[((i-37) & 0xffffffff) % 16];
|
||||
w = keyOut[((i-177) & 0xffffffff) % 16];
|
||||
keyOut[i] = rol8(x, 1) ^ y ^ rol8(z, 6) ^ rol8(w, 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
6
lib/plist/CMakeLists.txt
Executable file
6
lib/plist/CMakeLists.txt
Executable file
@@ -0,0 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.4.1)
|
||||
aux_source_directory(. plist_src)
|
||||
set(DIR_SRCS ${plist_src})
|
||||
add_library( plist
|
||||
STATIC
|
||||
${DIR_SRCS})
|
||||
119
lib/plist/base64.c
Executable file
119
lib/plist/base64.c
Executable file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* base64.c
|
||||
* base64 encode/decode implementation
|
||||
*
|
||||
* Copyright (c) 2011 Nikias Bassen, All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "base64.h"
|
||||
|
||||
static const char base64_str[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
static const char base64_pad = '=';
|
||||
|
||||
static const signed char base64_table[256] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1,
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
|
||||
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
|
||||
};
|
||||
|
||||
size_t base64encode(char *outbuf, const unsigned char *buf, size_t size)
|
||||
{
|
||||
if (!outbuf || !buf || (size <= 0)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t n = 0;
|
||||
size_t m = 0;
|
||||
unsigned char input[3];
|
||||
unsigned int output[4];
|
||||
while (n < size) {
|
||||
input[0] = buf[n];
|
||||
input[1] = (n+1 < size) ? buf[n+1] : 0;
|
||||
input[2] = (n+2 < size) ? buf[n+2] : 0;
|
||||
output[0] = input[0] >> 2;
|
||||
output[1] = ((input[0] & 3) << 4) + (input[1] >> 4);
|
||||
output[2] = ((input[1] & 15) << 2) + (input[2] >> 6);
|
||||
output[3] = input[2] & 63;
|
||||
outbuf[m++] = base64_str[(int)output[0]];
|
||||
outbuf[m++] = base64_str[(int)output[1]];
|
||||
outbuf[m++] = (n+1 < size) ? base64_str[(int)output[2]] : base64_pad;
|
||||
outbuf[m++] = (n+2 < size) ? base64_str[(int)output[3]] : base64_pad;
|
||||
n+=3;
|
||||
}
|
||||
outbuf[m] = 0; // 0-termination!
|
||||
return m;
|
||||
}
|
||||
|
||||
unsigned char *base64decode(const char *buf, size_t *size)
|
||||
{
|
||||
if (!buf || !size) return NULL;
|
||||
size_t len = (*size > 0) ? *size : strlen(buf);
|
||||
if (len <= 0) return NULL;
|
||||
unsigned char *outbuf = (unsigned char*)malloc((len/4)*3+3);
|
||||
const char *ptr = buf;
|
||||
int p = 0;
|
||||
int wv, w1, w2, w3, w4;
|
||||
int tmpval[4];
|
||||
int tmpcnt = 0;
|
||||
|
||||
do {
|
||||
while (ptr < buf+len && (*ptr == ' ' || *ptr == '\t' || *ptr == '\n' || *ptr == '\r')) {
|
||||
ptr++;
|
||||
}
|
||||
if (*ptr == '\0' || ptr >= buf+len) {
|
||||
break;
|
||||
}
|
||||
if ((wv = base64_table[(int)(unsigned char)*ptr++]) == -1) {
|
||||
continue;
|
||||
}
|
||||
tmpval[tmpcnt++] = wv;
|
||||
if (tmpcnt == 4) {
|
||||
tmpcnt = 0;
|
||||
w1 = tmpval[0];
|
||||
w2 = tmpval[1];
|
||||
w3 = tmpval[2];
|
||||
w4 = tmpval[3];
|
||||
|
||||
if (w1 >= 0 && w2 >= 0) {
|
||||
outbuf[p++] = (unsigned char)(((w1 << 2) + (w2 >> 4)) & 0xFF);
|
||||
}
|
||||
if (w2 >= 0 && w3 >= 0) {
|
||||
outbuf[p++] = (unsigned char)(((w2 << 4) + (w3 >> 2)) & 0xFF);
|
||||
}
|
||||
if (w3 >= 0 && w4 >= 0) {
|
||||
outbuf[p++] = (unsigned char)(((w3 << 6) + w4) & 0xFF);
|
||||
}
|
||||
}
|
||||
} while (1);
|
||||
|
||||
outbuf[p] = 0;
|
||||
*size = p;
|
||||
return outbuf;
|
||||
}
|
||||
28
lib/plist/base64.h
Executable file
28
lib/plist/base64.h
Executable file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* base64.h
|
||||
* base64 encode/decode implementation
|
||||
*
|
||||
* Copyright (c) 2011 Nikias Bassen, All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef BASE64_H
|
||||
#define BASE64_H
|
||||
#include <stdlib.h>
|
||||
|
||||
size_t base64encode(char *outbuf, const unsigned char *buf, size_t size);
|
||||
unsigned char *base64decode(const char *buf, size_t *size);
|
||||
|
||||
#endif
|
||||
1381
lib/plist/bplist.c
Executable file
1381
lib/plist/bplist.c
Executable file
File diff suppressed because it is too large
Load Diff
61
lib/plist/bytearray.c
Executable file
61
lib/plist/bytearray.c
Executable file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* bytearray.c
|
||||
* simple byte array implementation
|
||||
*
|
||||
* Copyright (c) 2011 Nikias Bassen, All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "bytearray.h"
|
||||
|
||||
#define PAGE_SIZE 4096
|
||||
|
||||
bytearray_t *byte_array_new(size_t initial)
|
||||
{
|
||||
bytearray_t *a = (bytearray_t*)malloc(sizeof(bytearray_t));
|
||||
a->capacity = (initial > PAGE_SIZE) ? (initial+(PAGE_SIZE-1)) & (~(PAGE_SIZE-1)) : PAGE_SIZE;
|
||||
a->data = malloc(a->capacity);
|
||||
a->len = 0;
|
||||
return a;
|
||||
}
|
||||
|
||||
void byte_array_free(bytearray_t *ba)
|
||||
{
|
||||
if (!ba) return;
|
||||
if (ba->data) {
|
||||
free(ba->data);
|
||||
}
|
||||
free(ba);
|
||||
}
|
||||
|
||||
void byte_array_grow(bytearray_t *ba, size_t amount)
|
||||
{
|
||||
size_t increase = (amount > PAGE_SIZE) ? (amount+(PAGE_SIZE-1)) & (~(PAGE_SIZE-1)) : PAGE_SIZE;
|
||||
ba->data = realloc(ba->data, ba->capacity + increase);
|
||||
ba->capacity += increase;
|
||||
}
|
||||
|
||||
void byte_array_append(bytearray_t *ba, void *buf, size_t len)
|
||||
{
|
||||
if (!ba || !ba->data || (len <= 0)) return;
|
||||
size_t remaining = ba->capacity-ba->len;
|
||||
if (len > remaining) {
|
||||
size_t needed = len - remaining;
|
||||
byte_array_grow(ba, needed);
|
||||
}
|
||||
memcpy(((char*)ba->data) + ba->len, buf, len);
|
||||
ba->len += len;
|
||||
}
|
||||
36
lib/plist/bytearray.h
Executable file
36
lib/plist/bytearray.h
Executable file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* bytearray.h
|
||||
* header file for simple byte array implementation
|
||||
*
|
||||
* Copyright (c) 2011 Nikias Bassen, All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef BYTEARRAY_H
|
||||
#define BYTEARRAY_H
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct bytearray_t {
|
||||
void *data;
|
||||
size_t len;
|
||||
size_t capacity;
|
||||
} bytearray_t;
|
||||
|
||||
bytearray_t *byte_array_new(size_t initial);
|
||||
void byte_array_free(bytearray_t *ba);
|
||||
void byte_array_grow(bytearray_t *ba, size_t amount);
|
||||
void byte_array_append(bytearray_t *ba, void *buf, size_t len);
|
||||
|
||||
#endif
|
||||
46
lib/plist/cnary.c
Executable file
46
lib/plist/cnary.c
Executable file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* cnary.c
|
||||
*
|
||||
* Created on: Mar 9, 2011
|
||||
* Author: posixninja
|
||||
*
|
||||
* Copyright (c) 2011 Joshua Hill. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "node.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
puts("Creating root node");
|
||||
node_t* root = node_create(NULL, NULL);
|
||||
|
||||
puts("Creating child 1 node");
|
||||
node_t* one = node_create(root, NULL);
|
||||
puts("Creating child 2 node");
|
||||
node_t* two = node_create(root, NULL);
|
||||
|
||||
puts("Creating child 3 node");
|
||||
node_t* three = node_create(one, NULL);
|
||||
|
||||
puts("Debugging root node");
|
||||
node_debug(root);
|
||||
|
||||
puts("Destroying root node");
|
||||
node_destroy(root);
|
||||
return 0;
|
||||
}
|
||||
140
lib/plist/hashtable.c
Executable file
140
lib/plist/hashtable.c
Executable file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* hashtable.c
|
||||
* really simple hash table implementation
|
||||
*
|
||||
* Copyright (c) 2011-2016 Nikias Bassen, All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include "hashtable.h"
|
||||
|
||||
hashtable_t* hash_table_new(hash_func_t hash_func, compare_func_t compare_func, free_func_t free_func)
|
||||
{
|
||||
hashtable_t* ht = (hashtable_t*)malloc(sizeof(hashtable_t));
|
||||
int i;
|
||||
for (i = 0; i < 4096; i++) {
|
||||
ht->entries[i] = NULL;
|
||||
}
|
||||
ht->count = 0;
|
||||
ht->hash_func = hash_func;
|
||||
ht->compare_func = compare_func;
|
||||
ht->free_func = free_func;
|
||||
return ht;
|
||||
}
|
||||
|
||||
void hash_table_destroy(hashtable_t *ht)
|
||||
{
|
||||
if (!ht) return;
|
||||
|
||||
int i = 0;
|
||||
for (i = 0; i < 4096; i++) {
|
||||
if (ht->entries[i]) {
|
||||
hashentry_t* e = ht->entries[i];
|
||||
while (e) {
|
||||
if (ht->free_func) {
|
||||
ht->free_func(e->value);
|
||||
}
|
||||
hashentry_t* old = e;
|
||||
e = e->next;
|
||||
free(old);
|
||||
}
|
||||
}
|
||||
}
|
||||
free(ht);
|
||||
}
|
||||
|
||||
void hash_table_insert(hashtable_t* ht, void *key, void *value)
|
||||
{
|
||||
if (!ht || !key) return;
|
||||
|
||||
unsigned int hash = ht->hash_func(key);
|
||||
|
||||
int idx0 = hash & 0xFFF;
|
||||
|
||||
// get the idx0 list
|
||||
hashentry_t* e = ht->entries[idx0];
|
||||
while (e) {
|
||||
if (ht->compare_func(e->key, key)) {
|
||||
// element already present. replace value.
|
||||
e->value = value;
|
||||
return;
|
||||
}
|
||||
e = e->next;
|
||||
}
|
||||
|
||||
// if we get here, the element is not yet in the list.
|
||||
|
||||
// make a new entry.
|
||||
hashentry_t* entry = (hashentry_t*)malloc(sizeof(hashentry_t));
|
||||
entry->key = key;
|
||||
entry->value = value;
|
||||
if (!ht->entries[idx0]) {
|
||||
// first entry
|
||||
entry->next = NULL;
|
||||
} else {
|
||||
// add to list
|
||||
entry->next = ht->entries[idx0];
|
||||
}
|
||||
ht->entries[idx0] = entry;
|
||||
ht->count++;
|
||||
}
|
||||
|
||||
void* hash_table_lookup(hashtable_t* ht, void *key)
|
||||
{
|
||||
if (!ht || !key) return NULL;
|
||||
unsigned int hash = ht->hash_func(key);
|
||||
|
||||
int idx0 = hash & 0xFFF;
|
||||
|
||||
hashentry_t* e = ht->entries[idx0];
|
||||
while (e) {
|
||||
if (ht->compare_func(e->key, key)) {
|
||||
return e->value;
|
||||
}
|
||||
e = e->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void hash_table_remove(hashtable_t* ht, void *key)
|
||||
{
|
||||
if (!ht || !key) return;
|
||||
|
||||
unsigned int hash = ht->hash_func(key);
|
||||
|
||||
int idx0 = hash & 0xFFF;
|
||||
|
||||
// get the idx0 list
|
||||
hashentry_t* e = ht->entries[idx0];
|
||||
hashentry_t* last = e;
|
||||
while (e) {
|
||||
if (ht->compare_func(e->key, key)) {
|
||||
// found element, remove it from the list
|
||||
hashentry_t* old = e;
|
||||
if (e == ht->entries[idx0]) {
|
||||
ht->entries[idx0] = e->next;
|
||||
} else {
|
||||
last->next = e->next;
|
||||
}
|
||||
if (ht->free_func) {
|
||||
ht->free_func(old->value);
|
||||
}
|
||||
free(old);
|
||||
return;
|
||||
}
|
||||
last = e;
|
||||
e = e->next;
|
||||
}
|
||||
}
|
||||
50
lib/plist/hashtable.h
Executable file
50
lib/plist/hashtable.h
Executable file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* hashtable.h
|
||||
* header file for really simple hash table implementation
|
||||
*
|
||||
* Copyright (c) 2011-2016 Nikias Bassen, All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef HASHTABLE_H
|
||||
#define HASHTABLE_H
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct hashentry_t {
|
||||
void *key;
|
||||
void *value;
|
||||
void *next;
|
||||
} hashentry_t;
|
||||
|
||||
typedef unsigned int(*hash_func_t)(const void* key);
|
||||
typedef int (*compare_func_t)(const void *a, const void *b);
|
||||
typedef void (*free_func_t)(void *ptr);
|
||||
|
||||
typedef struct hashtable_t {
|
||||
hashentry_t *entries[4096];
|
||||
size_t count;
|
||||
hash_func_t hash_func;
|
||||
compare_func_t compare_func;
|
||||
free_func_t free_func;
|
||||
} hashtable_t;
|
||||
|
||||
hashtable_t* hash_table_new(hash_func_t hash_func, compare_func_t compare_func, free_func_t free_func);
|
||||
void hash_table_destroy(hashtable_t *ht);
|
||||
|
||||
void hash_table_insert(hashtable_t* ht, void *key, void *value);
|
||||
void* hash_table_lookup(hashtable_t* ht, void *key);
|
||||
void hash_table_remove(hashtable_t* ht, void *key);
|
||||
|
||||
#endif
|
||||
47
lib/plist/list.c
Executable file
47
lib/plist/list.c
Executable file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* list.c
|
||||
*
|
||||
* Created on: Mar 8, 2011
|
||||
* Author: posixninja
|
||||
*
|
||||
* Copyright (c) 2011 Joshua Hill. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "list.h"
|
||||
|
||||
void list_init(list_t* list) {
|
||||
list->next = NULL;
|
||||
list->prev = list;
|
||||
}
|
||||
|
||||
|
||||
void list_destroy(list_t* list) {
|
||||
if(list) {
|
||||
free(list);
|
||||
}
|
||||
}
|
||||
|
||||
int list_add(list_t* list, object_t* object) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int list_remove(list_t* list, object_t* object) {
|
||||
return -1;
|
||||
}
|
||||
40
lib/plist/list.h
Executable file
40
lib/plist/list.h
Executable file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* list.h
|
||||
*
|
||||
* Created on: Mar 8, 2011
|
||||
* Author: posixninja
|
||||
*
|
||||
* Copyright (c) 2011 Joshua Hill. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef LIST_H_
|
||||
#define LIST_H_
|
||||
|
||||
#include "object.h"
|
||||
|
||||
typedef struct list_t {
|
||||
void* next;
|
||||
void* prev;
|
||||
} list_t;
|
||||
|
||||
void list_init(struct list_t* list);
|
||||
void list_destroy(struct list_t* list);
|
||||
|
||||
int list_add(struct list_t* list, struct object_t* object);
|
||||
int list_remove(struct list_t* list, struct object_t* object);
|
||||
|
||||
#endif /* LIST_H_ */
|
||||
216
lib/plist/node.c
Executable file
216
lib/plist/node.c
Executable file
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* node.c
|
||||
*
|
||||
* Created on: Mar 7, 2011
|
||||
* Author: posixninja
|
||||
*
|
||||
* Copyright (c) 2011 Joshua Hill. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "node.h"
|
||||
#include "node_list.h"
|
||||
|
||||
void node_destroy(node_t* node) {
|
||||
if(!node) return;
|
||||
|
||||
if (node->children && node->children->count > 0) {
|
||||
node_t* ch;
|
||||
while ((ch = node->children->begin)) {
|
||||
node_list_remove(node->children, ch);
|
||||
node_destroy(ch);
|
||||
}
|
||||
}
|
||||
node_list_destroy(node->children);
|
||||
node->children = NULL;
|
||||
|
||||
free(node);
|
||||
}
|
||||
|
||||
node_t* node_create(node_t* parent, void* data) {
|
||||
int error = 0;
|
||||
|
||||
node_t* node = (node_t*) malloc(sizeof(node_t));
|
||||
if(node == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
memset(node, '\0', sizeof(node_t));
|
||||
|
||||
node->data = data;
|
||||
node->next = NULL;
|
||||
node->prev = NULL;
|
||||
node->count = 0;
|
||||
node->parent = NULL;
|
||||
node->children = NULL;
|
||||
|
||||
// Pass NULL to create a root node
|
||||
if(parent != NULL) {
|
||||
// This is a child node so attach it to it's parent
|
||||
error = node_attach(parent, node);
|
||||
if(error < 0) {
|
||||
// Unable to attach nodes
|
||||
printf("ERROR: %d \"Unable to attach nodes\"\n", error);
|
||||
node_destroy(node);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
int node_attach(node_t* parent, node_t* child) {
|
||||
if (!parent || !child) return -1;
|
||||
child->parent = parent;
|
||||
if(!parent->children) {
|
||||
parent->children = node_list_create();
|
||||
}
|
||||
int res = node_list_add(parent->children, child);
|
||||
if (res == 0) {
|
||||
parent->count++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int node_detach(node_t* parent, node_t* child) {
|
||||
if (!parent || !child) return -1;
|
||||
int node_index = node_list_remove(parent->children, child);
|
||||
if (node_index >= 0) {
|
||||
parent->count--;
|
||||
}
|
||||
return node_index;
|
||||
}
|
||||
|
||||
int node_insert(node_t* parent, unsigned int node_index, node_t* child)
|
||||
{
|
||||
if (!parent || !child) return -1;
|
||||
child->parent = parent;
|
||||
if(!parent->children) {
|
||||
parent->children = node_list_create();
|
||||
}
|
||||
int res = node_list_insert(parent->children, node_index, child);
|
||||
if (res == 0) {
|
||||
parent->count++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void _node_debug(node_t* node, unsigned int depth) {
|
||||
unsigned int i = 0;
|
||||
node_t* current = NULL;
|
||||
for(i = 0; i < depth; i++) {
|
||||
printf("\t");
|
||||
}
|
||||
if(!node->parent) {
|
||||
printf("ROOT\n");
|
||||
}
|
||||
|
||||
if(!node->children && node->parent) {
|
||||
printf("LEAF\n");
|
||||
} else {
|
||||
if(node->parent) {
|
||||
printf("NODE\n");
|
||||
}
|
||||
for (current = node_first_child(node); current; current = node_next_sibling(current)) {
|
||||
_node_debug(current, depth+1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void node_debug(node_t* node)
|
||||
{
|
||||
_node_debug(node, 0);
|
||||
}
|
||||
|
||||
unsigned int node_n_children(struct node_t* node)
|
||||
{
|
||||
if (!node) return 0;
|
||||
return node->count;
|
||||
}
|
||||
|
||||
node_t* node_nth_child(struct node_t* node, unsigned int n)
|
||||
{
|
||||
if (!node || !node->children || !node->children->begin) return NULL;
|
||||
unsigned int node_index = 0;
|
||||
int found = 0;
|
||||
node_t *ch;
|
||||
for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
|
||||
if (node_index++ == n) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
return NULL;
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
|
||||
node_t* node_first_child(struct node_t* node)
|
||||
{
|
||||
if (!node || !node->children) return NULL;
|
||||
return node->children->begin;
|
||||
}
|
||||
|
||||
node_t* node_prev_sibling(struct node_t* node)
|
||||
{
|
||||
if (!node) return NULL;
|
||||
return node->prev;
|
||||
}
|
||||
|
||||
node_t* node_next_sibling(struct node_t* node)
|
||||
{
|
||||
if (!node) return NULL;
|
||||
return node->next;
|
||||
}
|
||||
|
||||
int node_child_position(struct node_t* parent, node_t* child)
|
||||
{
|
||||
if (!parent || !parent->children || !parent->children->begin || !child) return -1;
|
||||
int node_index = 0;
|
||||
int found = 0;
|
||||
node_t *ch;
|
||||
for (ch = node_first_child(parent); ch; ch = node_next_sibling(ch)) {
|
||||
if (ch == child) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
node_index++;
|
||||
}
|
||||
if (!found) {
|
||||
return -1;
|
||||
}
|
||||
return node_index;
|
||||
}
|
||||
|
||||
node_t* node_copy_deep(node_t* node, copy_func_t copy_func)
|
||||
{
|
||||
if (!node) return NULL;
|
||||
void *data = NULL;
|
||||
if (copy_func) {
|
||||
data = copy_func(node->data);
|
||||
}
|
||||
node_t* copy = node_create(NULL, data);
|
||||
node_t* ch;
|
||||
for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
|
||||
node_t* cc = node_copy_deep(ch, copy_func);
|
||||
node_attach(copy, cc);
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
65
lib/plist/node.h
Executable file
65
lib/plist/node.h
Executable file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* node.h
|
||||
*
|
||||
* Created on: Mar 7, 2011
|
||||
* Author: posixninja
|
||||
*
|
||||
* Copyright (c) 2011 Joshua Hill. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef NODE_H_
|
||||
#define NODE_H_
|
||||
|
||||
#include "object.h"
|
||||
|
||||
#define NODE_TYPE 1;
|
||||
|
||||
struct node_list_t;
|
||||
|
||||
// This class implements the abstract iterator class
|
||||
typedef struct node_t {
|
||||
// Super class
|
||||
struct node_t* next;
|
||||
struct node_t* prev;
|
||||
unsigned int count;
|
||||
|
||||
// Local Members
|
||||
void *data;
|
||||
struct node_t* parent;
|
||||
struct node_list_t* children;
|
||||
} node_t;
|
||||
|
||||
void node_destroy(struct node_t* node);
|
||||
struct node_t* node_create(struct node_t* parent, void* data);
|
||||
|
||||
int node_attach(struct node_t* parent, struct node_t* child);
|
||||
int node_detach(struct node_t* parent, struct node_t* child);
|
||||
int node_insert(struct node_t* parent, unsigned int index, struct node_t* child);
|
||||
|
||||
unsigned int node_n_children(struct node_t* node);
|
||||
node_t* node_nth_child(struct node_t* node, unsigned int n);
|
||||
node_t* node_first_child(struct node_t* node);
|
||||
node_t* node_prev_sibling(struct node_t* node);
|
||||
node_t* node_next_sibling(struct node_t* node);
|
||||
int node_child_position(struct node_t* parent, node_t* child);
|
||||
|
||||
typedef void* (*copy_func_t)(const void *src);
|
||||
node_t* node_copy_deep(node_t* node, copy_func_t copy_func);
|
||||
|
||||
void node_debug(struct node_t* node);
|
||||
|
||||
#endif /* NODE_H_ */
|
||||
154
lib/plist/node_list.c
Executable file
154
lib/plist/node_list.c
Executable file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* node_list.c
|
||||
*
|
||||
* Created on: Mar 8, 2011
|
||||
* Author: posixninja
|
||||
*
|
||||
* Copyright (c) 2011 Joshua Hill. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "list.h"
|
||||
#include "node.h"
|
||||
#include "node_list.h"
|
||||
|
||||
void node_list_destroy(node_list_t* list) {
|
||||
if(list != NULL) {
|
||||
list_destroy((list_t*) list);
|
||||
}
|
||||
}
|
||||
|
||||
node_list_t* node_list_create() {
|
||||
node_list_t* list = (node_list_t*) malloc(sizeof(node_list_t));
|
||||
if(list == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
memset(list, '\0', sizeof(node_list_t));
|
||||
|
||||
// Initialize structure
|
||||
list_init((list_t*) list);
|
||||
list->count = 0;
|
||||
return list;
|
||||
}
|
||||
|
||||
int node_list_add(node_list_t* list, node_t* node) {
|
||||
if (!list || !node) return -1;
|
||||
|
||||
// Find the last element in the list
|
||||
node_t* last = list->end;
|
||||
|
||||
// Setup our new node as the new last element
|
||||
node->next = NULL;
|
||||
node->prev = last;
|
||||
|
||||
// Set the next element of our old "last" element
|
||||
if (last) {
|
||||
// but only if the node list is not empty
|
||||
last->next = node;
|
||||
}
|
||||
|
||||
// Set the lists prev to the new last element
|
||||
list->end = node;
|
||||
|
||||
// Increment our node count for this list
|
||||
list->count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int node_list_insert(node_list_t* list, unsigned int node_index, node_t* node) {
|
||||
if (!list || !node) return -1;
|
||||
if (node_index >= list->count) {
|
||||
return node_list_add(list, node);
|
||||
}
|
||||
|
||||
// Get the first element in the list
|
||||
node_t* cur = list->begin;
|
||||
|
||||
unsigned int pos = 0;
|
||||
node_t* prev = NULL;
|
||||
|
||||
if (node_index > 0) {
|
||||
while (pos < node_index) {
|
||||
prev = cur;
|
||||
cur = cur->next;
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
if (prev) {
|
||||
// Set previous node
|
||||
node->prev = prev;
|
||||
// Set next node of our new node to next node of the previous node
|
||||
node->next = prev->next;
|
||||
// Set next node of previous node to our new node
|
||||
prev->next = node;
|
||||
} else {
|
||||
node->prev = NULL;
|
||||
// get old first element in list
|
||||
node->next = list->begin;
|
||||
// set new node as first element in list
|
||||
list->begin = node;
|
||||
}
|
||||
|
||||
if (node->next == NULL) {
|
||||
// Set the lists prev to the new last element
|
||||
list->end = node;
|
||||
} else {
|
||||
// set prev of the new next element to our node
|
||||
node->next->prev = node;
|
||||
}
|
||||
|
||||
// Increment our node count for this list
|
||||
list->count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int node_list_remove(node_list_t* list, node_t* node) {
|
||||
if (!list || !node) return -1;
|
||||
if (list->count == 0) return -1;
|
||||
|
||||
int node_index = 0;
|
||||
node_t* n;
|
||||
for (n = list->begin; n; n = n->next) {
|
||||
if (node == n) {
|
||||
node_t* newnode = node->next;
|
||||
if (node->prev) {
|
||||
node->prev->next = newnode;
|
||||
if (newnode) {
|
||||
newnode->prev = node->prev;
|
||||
} else {
|
||||
// last element in the list
|
||||
list->end = node->prev;
|
||||
}
|
||||
} else {
|
||||
// we just removed the first element
|
||||
if (newnode) {
|
||||
newnode->prev = NULL;
|
||||
}
|
||||
list->begin = newnode;
|
||||
}
|
||||
list->count--;
|
||||
return node_index;
|
||||
}
|
||||
node_index++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
47
lib/plist/node_list.h
Executable file
47
lib/plist/node_list.h
Executable file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* node_list.h
|
||||
*
|
||||
* Created on: Mar 8, 2011
|
||||
* Author: posixninja
|
||||
*
|
||||
* Copyright (c) 2011 Joshua Hill. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef NODE_LIST_H_
|
||||
#define NODE_LIST_H_
|
||||
|
||||
struct node_t;
|
||||
|
||||
// This class implements the list_t abstract class
|
||||
typedef struct node_list_t {
|
||||
// list_t members
|
||||
struct node_t* begin;
|
||||
struct node_t* end;
|
||||
|
||||
// node_list_t members
|
||||
unsigned int count;
|
||||
|
||||
} node_list_t;
|
||||
|
||||
void node_list_destroy(struct node_list_t* list);
|
||||
struct node_list_t* node_list_create();
|
||||
|
||||
int node_list_add(node_list_t* list, node_t* node);
|
||||
int node_list_insert(node_list_t* list, unsigned int index, node_t* node);
|
||||
int node_list_remove(node_list_t* list, node_t* node);
|
||||
|
||||
#endif /* NODE_LIST_H_ */
|
||||
41
lib/plist/object.h
Executable file
41
lib/plist/object.h
Executable file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* object.h
|
||||
*
|
||||
* Created on: Mar 8, 2011
|
||||
* Author: posixninja
|
||||
*
|
||||
* Copyright (c) 2011 Joshua Hill. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef OBJECT_H_
|
||||
#define OBJECT_H_
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
typedef struct object_t {
|
||||
void* value;
|
||||
unsigned int type;
|
||||
unsigned int size;
|
||||
} object_t;
|
||||
|
||||
#endif /* OBJECT_H_ */
|
||||
963
lib/plist/plist.c
Executable file
963
lib/plist/plist.c
Executable file
@@ -0,0 +1,963 @@
|
||||
/*
|
||||
* plist.c
|
||||
* Builds plist XML structures
|
||||
*
|
||||
* Copyright (c) 2009-2016 Nikias Bassen All Rights Reserved.
|
||||
* Copyright (c) 2010-2015 Martin Szulecki All Rights Reserved.
|
||||
* Copyright (c) 2008 Zach C. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "plist.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#include "node.h"
|
||||
#include "hashtable.h"
|
||||
|
||||
extern void plist_xml_init(void);
|
||||
extern void plist_xml_deinit(void);
|
||||
extern void plist_bin_init(void);
|
||||
extern void plist_bin_deinit(void);
|
||||
|
||||
static void internal_plist_init(void)
|
||||
{
|
||||
plist_bin_init();
|
||||
plist_xml_init();
|
||||
}
|
||||
|
||||
static void internal_plist_deinit(void)
|
||||
{
|
||||
plist_bin_deinit();
|
||||
plist_xml_deinit();
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
typedef volatile struct {
|
||||
LONG lock;
|
||||
int state;
|
||||
} thread_once_t;
|
||||
|
||||
static thread_once_t init_once = {0, 0};
|
||||
static thread_once_t deinit_once = {0, 0};
|
||||
|
||||
void thread_once(thread_once_t *once_control, void (*init_routine)(void))
|
||||
{
|
||||
while (InterlockedExchange(&(once_control->lock), 1) != 0) {
|
||||
Sleep(1);
|
||||
}
|
||||
if (!once_control->state) {
|
||||
once_control->state = 1;
|
||||
init_routine();
|
||||
}
|
||||
InterlockedExchange(&(once_control->lock), 0);
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID lpReserved)
|
||||
{
|
||||
switch (dwReason) {
|
||||
case DLL_PROCESS_ATTACH:
|
||||
thread_once(&init_once, internal_plist_init);
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
thread_once(&deinit_once, internal_plist_deinit);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static pthread_once_t init_once = PTHREAD_ONCE_INIT;
|
||||
static pthread_once_t deinit_once = PTHREAD_ONCE_INIT;
|
||||
|
||||
static void __attribute__((constructor)) libplist_initialize(void)
|
||||
{
|
||||
pthread_once(&init_once, internal_plist_init);
|
||||
}
|
||||
|
||||
static void __attribute__((destructor)) libplist_deinitialize(void)
|
||||
{
|
||||
pthread_once(&deinit_once, internal_plist_deinit);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
PLIST_API int plist_is_binary(const char *plist_data, uint32_t length)
|
||||
{
|
||||
if (length < 8) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (memcmp(plist_data, "bplist00", 8) == 0);
|
||||
}
|
||||
|
||||
|
||||
PLIST_API void plist_from_memory(const char *plist_data, uint32_t length, plist_t * plist)
|
||||
{
|
||||
if (length < 8) {
|
||||
*plist = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (plist_is_binary(plist_data, length)) {
|
||||
plist_from_bin(plist_data, length, plist);
|
||||
} else {
|
||||
plist_from_xml(plist_data, length, plist);
|
||||
}
|
||||
}
|
||||
|
||||
plist_t plist_new_node(plist_data_t data)
|
||||
{
|
||||
return (plist_t) node_create(NULL, data);
|
||||
}
|
||||
|
||||
plist_data_t plist_get_data(const plist_t node)
|
||||
{
|
||||
if (!node)
|
||||
return NULL;
|
||||
return ((node_t*)node)->data;
|
||||
}
|
||||
|
||||
plist_data_t plist_new_plist_data(void)
|
||||
{
|
||||
plist_data_t data = (plist_data_t) calloc(sizeof(struct plist_data_s), 1);
|
||||
return data;
|
||||
}
|
||||
|
||||
static unsigned int dict_key_hash(const void *data)
|
||||
{
|
||||
plist_data_t keydata = (plist_data_t)data;
|
||||
unsigned int hash = 5381;
|
||||
size_t i;
|
||||
char *str = keydata->strval;
|
||||
for (i = 0; i < keydata->length; str++, i++) {
|
||||
hash = ((hash << 5) + hash) + *str;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
static int dict_key_compare(const void* a, const void* b)
|
||||
{
|
||||
plist_data_t data_a = (plist_data_t)a;
|
||||
plist_data_t data_b = (plist_data_t)b;
|
||||
if (data_a->strval == NULL || data_b->strval == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
if (data_a->length != data_b->length) {
|
||||
return FALSE;
|
||||
}
|
||||
return (strcmp(data_a->strval, data_b->strval) == 0) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
void plist_free_data(plist_data_t data)
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
switch (data->type)
|
||||
{
|
||||
case PLIST_KEY:
|
||||
case PLIST_STRING:
|
||||
free(data->strval);
|
||||
break;
|
||||
case PLIST_DATA:
|
||||
free(data->buff);
|
||||
break;
|
||||
case PLIST_DICT:
|
||||
hash_table_destroy(data->hashtable);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
}
|
||||
|
||||
static int plist_free_node(node_t* node)
|
||||
{
|
||||
plist_data_t data = NULL;
|
||||
int node_index = node_detach(node->parent, node);
|
||||
data = plist_get_data(node);
|
||||
plist_free_data(data);
|
||||
node->data = NULL;
|
||||
|
||||
node_t *ch;
|
||||
for (ch = node_first_child(node); ch; ) {
|
||||
node_t *next = node_next_sibling(ch);
|
||||
plist_free_node(ch);
|
||||
ch = next;
|
||||
}
|
||||
|
||||
node_destroy(node);
|
||||
|
||||
return node_index;
|
||||
}
|
||||
|
||||
PLIST_API plist_t plist_new_dict(void)
|
||||
{
|
||||
plist_data_t data = plist_new_plist_data();
|
||||
data->type = PLIST_DICT;
|
||||
return plist_new_node(data);
|
||||
}
|
||||
|
||||
PLIST_API plist_t plist_new_array(void)
|
||||
{
|
||||
plist_data_t data = plist_new_plist_data();
|
||||
data->type = PLIST_ARRAY;
|
||||
return plist_new_node(data);
|
||||
}
|
||||
|
||||
//These nodes should not be handled by users
|
||||
static plist_t plist_new_key(const char *val)
|
||||
{
|
||||
plist_data_t data = plist_new_plist_data();
|
||||
data->type = PLIST_KEY;
|
||||
data->strval = strdup(val);
|
||||
data->length = strlen(val);
|
||||
return plist_new_node(data);
|
||||
}
|
||||
|
||||
PLIST_API plist_t plist_new_string(const char *val)
|
||||
{
|
||||
plist_data_t data = plist_new_plist_data();
|
||||
data->type = PLIST_STRING;
|
||||
data->strval = strdup(val);
|
||||
data->length = strlen(val);
|
||||
return plist_new_node(data);
|
||||
}
|
||||
|
||||
PLIST_API plist_t plist_new_bool(uint8_t val)
|
||||
{
|
||||
plist_data_t data = plist_new_plist_data();
|
||||
data->type = PLIST_BOOLEAN;
|
||||
data->boolval = val;
|
||||
data->length = sizeof(uint8_t);
|
||||
return plist_new_node(data);
|
||||
}
|
||||
|
||||
PLIST_API plist_t plist_new_uint(uint64_t val)
|
||||
{
|
||||
plist_data_t data = plist_new_plist_data();
|
||||
data->type = PLIST_UINT;
|
||||
data->intval = val;
|
||||
data->length = sizeof(uint64_t);
|
||||
return plist_new_node(data);
|
||||
}
|
||||
|
||||
PLIST_API plist_t plist_new_uid(uint64_t val)
|
||||
{
|
||||
plist_data_t data = plist_new_plist_data();
|
||||
data->type = PLIST_UID;
|
||||
data->intval = val;
|
||||
data->length = sizeof(uint64_t);
|
||||
return plist_new_node(data);
|
||||
}
|
||||
|
||||
PLIST_API plist_t plist_new_real(double val)
|
||||
{
|
||||
plist_data_t data = plist_new_plist_data();
|
||||
data->type = PLIST_REAL;
|
||||
data->realval = val;
|
||||
data->length = sizeof(double);
|
||||
return plist_new_node(data);
|
||||
}
|
||||
|
||||
PLIST_API plist_t plist_new_data(const char *val, uint64_t length)
|
||||
{
|
||||
plist_data_t data = plist_new_plist_data();
|
||||
data->type = PLIST_DATA;
|
||||
data->buff = (uint8_t *) malloc(length);
|
||||
memcpy(data->buff, val, length);
|
||||
data->length = length;
|
||||
return plist_new_node(data);
|
||||
}
|
||||
|
||||
PLIST_API plist_t plist_new_date(int32_t sec, int32_t usec)
|
||||
{
|
||||
plist_data_t data = plist_new_plist_data();
|
||||
data->type = PLIST_DATE;
|
||||
data->realval = (double)sec + (double)usec / 1000000;
|
||||
data->length = sizeof(double);
|
||||
return plist_new_node(data);
|
||||
}
|
||||
|
||||
PLIST_API void plist_free(plist_t plist)
|
||||
{
|
||||
if (plist)
|
||||
{
|
||||
plist_free_node(plist);
|
||||
}
|
||||
}
|
||||
|
||||
static void plist_copy_node(node_t *node, void *parent_node_ptr)
|
||||
{
|
||||
plist_type node_type = PLIST_NONE;
|
||||
plist_t newnode = NULL;
|
||||
plist_data_t data = plist_get_data(node);
|
||||
plist_data_t newdata = plist_new_plist_data();
|
||||
|
||||
assert(data); // plist should always have data
|
||||
|
||||
memcpy(newdata, data, sizeof(struct plist_data_s));
|
||||
|
||||
node_type = plist_get_node_type(node);
|
||||
switch (node_type) {
|
||||
case PLIST_DATA:
|
||||
newdata->buff = (uint8_t *) malloc(data->length);
|
||||
memcpy(newdata->buff, data->buff, data->length);
|
||||
break;
|
||||
case PLIST_KEY:
|
||||
case PLIST_STRING:
|
||||
newdata->strval = strdup((char *) data->strval);
|
||||
break;
|
||||
case PLIST_DICT:
|
||||
if (data->hashtable) {
|
||||
hashtable_t* ht = hash_table_new(dict_key_hash, dict_key_compare, NULL);
|
||||
assert(ht);
|
||||
plist_t current = NULL;
|
||||
for (current = (plist_t)node_first_child(node);
|
||||
ht && current;
|
||||
current = (plist_t)node_next_sibling(node_next_sibling(current)))
|
||||
{
|
||||
hash_table_insert(ht, ((node_t*)current)->data, node_next_sibling(current));
|
||||
}
|
||||
newdata->hashtable = ht;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
newnode = plist_new_node(newdata);
|
||||
|
||||
if (*(plist_t*)parent_node_ptr)
|
||||
{
|
||||
node_attach(*(plist_t*)parent_node_ptr, newnode);
|
||||
}
|
||||
else
|
||||
{
|
||||
*(plist_t*)parent_node_ptr = newnode;
|
||||
}
|
||||
|
||||
node_t *ch;
|
||||
for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
|
||||
plist_copy_node(ch, &newnode);
|
||||
}
|
||||
}
|
||||
|
||||
PLIST_API plist_t plist_copy(plist_t node)
|
||||
{
|
||||
plist_t copied = NULL;
|
||||
plist_copy_node(node, &copied);
|
||||
return copied;
|
||||
}
|
||||
|
||||
PLIST_API uint32_t plist_array_get_size(plist_t node)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
if (node && PLIST_ARRAY == plist_get_node_type(node))
|
||||
{
|
||||
ret = node_n_children(node);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
PLIST_API plist_t plist_array_get_item(plist_t node, uint32_t n)
|
||||
{
|
||||
plist_t ret = NULL;
|
||||
if (node && PLIST_ARRAY == plist_get_node_type(node))
|
||||
{
|
||||
ret = (plist_t)node_nth_child(node, n);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
PLIST_API uint32_t plist_array_get_item_index(plist_t node)
|
||||
{
|
||||
plist_t father = plist_get_parent(node);
|
||||
if (PLIST_ARRAY == plist_get_node_type(father))
|
||||
{
|
||||
return node_child_position(father, node);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
PLIST_API void plist_array_set_item(plist_t node, plist_t item, uint32_t n)
|
||||
{
|
||||
if (node && PLIST_ARRAY == plist_get_node_type(node))
|
||||
{
|
||||
plist_t old_item = plist_array_get_item(node, n);
|
||||
if (old_item)
|
||||
{
|
||||
int idx = plist_free_node(old_item);
|
||||
if (idx < 0) {
|
||||
node_attach(node, item);
|
||||
} else {
|
||||
node_insert(node, idx, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
PLIST_API void plist_array_append_item(plist_t node, plist_t item)
|
||||
{
|
||||
if (node && PLIST_ARRAY == plist_get_node_type(node))
|
||||
{
|
||||
node_attach(node, item);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
PLIST_API void plist_array_insert_item(plist_t node, plist_t item, uint32_t n)
|
||||
{
|
||||
if (node && PLIST_ARRAY == plist_get_node_type(node))
|
||||
{
|
||||
node_insert(node, n, item);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
PLIST_API void plist_array_remove_item(plist_t node, uint32_t n)
|
||||
{
|
||||
if (node && PLIST_ARRAY == plist_get_node_type(node))
|
||||
{
|
||||
plist_t old_item = plist_array_get_item(node, n);
|
||||
if (old_item)
|
||||
{
|
||||
plist_free(old_item);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
PLIST_API uint32_t plist_dict_get_size(plist_t node)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
if (node && PLIST_DICT == plist_get_node_type(node))
|
||||
{
|
||||
ret = node_n_children(node) / 2;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
PLIST_API void plist_dict_new_iter(plist_t node, plist_dict_iter *iter)
|
||||
{
|
||||
if (iter && *iter == NULL)
|
||||
{
|
||||
*iter = malloc(sizeof(node_t*));
|
||||
*((node_t**)(*iter)) = node_first_child(node);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
PLIST_API void plist_dict_next_item(plist_t node, plist_dict_iter iter, char **key, plist_t *val)
|
||||
{
|
||||
node_t** iter_node = (node_t**)iter;
|
||||
|
||||
if (key)
|
||||
{
|
||||
*key = NULL;
|
||||
}
|
||||
if (val)
|
||||
{
|
||||
*val = NULL;
|
||||
}
|
||||
|
||||
if (node && PLIST_DICT == plist_get_node_type(node) && *iter_node)
|
||||
{
|
||||
if (key)
|
||||
{
|
||||
plist_get_key_val((plist_t)(*iter_node), key);
|
||||
}
|
||||
*iter_node = node_next_sibling(*iter_node);
|
||||
if (val)
|
||||
{
|
||||
*val = (plist_t)(*iter_node);
|
||||
}
|
||||
*iter_node = node_next_sibling(*iter_node);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
PLIST_API void plist_dict_get_item_key(plist_t node, char **key)
|
||||
{
|
||||
plist_t father = plist_get_parent(node);
|
||||
if (PLIST_DICT == plist_get_node_type(father))
|
||||
{
|
||||
plist_get_key_val( (plist_t) node_prev_sibling(node), key);
|
||||
}
|
||||
}
|
||||
|
||||
PLIST_API plist_t plist_dict_get_item(plist_t node, const char* key)
|
||||
{
|
||||
plist_t ret = NULL;
|
||||
|
||||
if (node && PLIST_DICT == plist_get_node_type(node))
|
||||
{
|
||||
plist_data_t data = plist_get_data(node);
|
||||
hashtable_t *ht = (hashtable_t*)data->hashtable;
|
||||
if (ht) {
|
||||
struct plist_data_s sdata;
|
||||
sdata.strval = (char*)key;
|
||||
sdata.length = strlen(key);
|
||||
ret = (plist_t)hash_table_lookup(ht, &sdata);
|
||||
} else {
|
||||
plist_t current = NULL;
|
||||
for (current = (plist_t)node_first_child(node);
|
||||
current;
|
||||
current = (plist_t)node_next_sibling(node_next_sibling(current)))
|
||||
{
|
||||
data = plist_get_data(current);
|
||||
assert( PLIST_KEY == plist_get_node_type(current) );
|
||||
|
||||
if (data && !strcmp(key, data->strval))
|
||||
{
|
||||
ret = (plist_t)node_next_sibling(current);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
PLIST_API void plist_dict_set_item(plist_t node, const char* key, plist_t item)
|
||||
{
|
||||
if (node && PLIST_DICT == plist_get_node_type(node)) {
|
||||
node_t* old_item = plist_dict_get_item(node, key);
|
||||
plist_t key_node = NULL;
|
||||
if (old_item) {
|
||||
int idx = plist_free_node(old_item);
|
||||
if (idx < 0) {
|
||||
node_attach(node, item);
|
||||
} else {
|
||||
node_insert(node, idx, item);
|
||||
}
|
||||
key_node = node_prev_sibling(item);
|
||||
} else {
|
||||
key_node = plist_new_key(key);
|
||||
node_attach(node, key_node);
|
||||
node_attach(node, item);
|
||||
}
|
||||
|
||||
hashtable_t *ht = ((plist_data_t)((node_t*)node)->data)->hashtable;
|
||||
if (ht) {
|
||||
/* store pointer to item in hash table */
|
||||
hash_table_insert(ht, (plist_data_t)((node_t*)key_node)->data, item);
|
||||
} else {
|
||||
if (((node_t*)node)->count > 500) {
|
||||
/* make new hash table */
|
||||
ht = hash_table_new(dict_key_hash, dict_key_compare, NULL);
|
||||
/* calculate the hashes for all entries we have so far */
|
||||
plist_t current = NULL;
|
||||
for (current = (plist_t)node_first_child(node);
|
||||
ht && current;
|
||||
current = (plist_t)node_next_sibling(node_next_sibling(current)))
|
||||
{
|
||||
hash_table_insert(ht, ((node_t*)current)->data, node_next_sibling(current));
|
||||
}
|
||||
((plist_data_t)((node_t*)node)->data)->hashtable = ht;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
PLIST_API void plist_dict_insert_item(plist_t node, const char* key, plist_t item)
|
||||
{
|
||||
plist_dict_set_item(node, key, item);
|
||||
}
|
||||
|
||||
PLIST_API void plist_dict_remove_item(plist_t node, const char* key)
|
||||
{
|
||||
if (node && PLIST_DICT == plist_get_node_type(node))
|
||||
{
|
||||
plist_t old_item = plist_dict_get_item(node, key);
|
||||
if (old_item)
|
||||
{
|
||||
plist_t key_node = node_prev_sibling(old_item);
|
||||
hashtable_t* ht = ((plist_data_t)((node_t*)node)->data)->hashtable;
|
||||
if (ht) {
|
||||
hash_table_remove(ht, ((node_t*)key_node)->data);
|
||||
}
|
||||
plist_free(key_node);
|
||||
plist_free(old_item);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
PLIST_API void plist_dict_merge(plist_t *target, plist_t source)
|
||||
{
|
||||
if (!target || !*target || (plist_get_node_type(*target) != PLIST_DICT) || !source || (plist_get_node_type(source) != PLIST_DICT))
|
||||
return;
|
||||
|
||||
char* key = NULL;
|
||||
plist_dict_iter it = NULL;
|
||||
plist_t subnode = NULL;
|
||||
plist_dict_new_iter(source, &it);
|
||||
if (!it)
|
||||
return;
|
||||
|
||||
do {
|
||||
plist_dict_next_item(source, it, &key, &subnode);
|
||||
if (!key)
|
||||
break;
|
||||
|
||||
plist_dict_set_item(*target, key, plist_copy(subnode));
|
||||
free(key);
|
||||
key = NULL;
|
||||
} while (1);
|
||||
free(it);
|
||||
}
|
||||
|
||||
PLIST_API plist_t plist_access_pathv(plist_t plist, uint32_t length, va_list v)
|
||||
{
|
||||
plist_t current = plist;
|
||||
plist_type type = PLIST_NONE;
|
||||
uint32_t i = 0;
|
||||
|
||||
for (i = 0; i < length && current; i++)
|
||||
{
|
||||
type = plist_get_node_type(current);
|
||||
|
||||
if (type == PLIST_ARRAY)
|
||||
{
|
||||
uint32_t n = va_arg(v, uint32_t);
|
||||
current = plist_array_get_item(current, n);
|
||||
}
|
||||
else if (type == PLIST_DICT)
|
||||
{
|
||||
const char* key = va_arg(v, const char*);
|
||||
current = plist_dict_get_item(current, key);
|
||||
}
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
PLIST_API plist_t plist_access_path(plist_t plist, uint32_t length, ...)
|
||||
{
|
||||
plist_t ret = NULL;
|
||||
va_list v;
|
||||
|
||||
va_start(v, length);
|
||||
ret = plist_access_pathv(plist, length, v);
|
||||
va_end(v);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void plist_get_type_and_value(plist_t node, plist_type * type, void *value, uint64_t * length)
|
||||
{
|
||||
plist_data_t data = NULL;
|
||||
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
data = plist_get_data(node);
|
||||
|
||||
*type = data->type;
|
||||
*length = data->length;
|
||||
|
||||
switch (*type)
|
||||
{
|
||||
case PLIST_BOOLEAN:
|
||||
*((char *) value) = data->boolval;
|
||||
break;
|
||||
case PLIST_UINT:
|
||||
case PLIST_UID:
|
||||
*((uint64_t *) value) = data->intval;
|
||||
break;
|
||||
case PLIST_REAL:
|
||||
case PLIST_DATE:
|
||||
*((double *) value) = data->realval;
|
||||
break;
|
||||
case PLIST_KEY:
|
||||
case PLIST_STRING:
|
||||
*((char **) value) = strdup(data->strval);
|
||||
break;
|
||||
case PLIST_DATA:
|
||||
*((uint8_t **) value) = (uint8_t *) malloc(*length * sizeof(uint8_t));
|
||||
memcpy(*((uint8_t **) value), data->buff, *length * sizeof(uint8_t));
|
||||
break;
|
||||
case PLIST_ARRAY:
|
||||
case PLIST_DICT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PLIST_API plist_t plist_get_parent(plist_t node)
|
||||
{
|
||||
return node ? (plist_t) ((node_t*) node)->parent : NULL;
|
||||
}
|
||||
|
||||
PLIST_API plist_type plist_get_node_type(plist_t node)
|
||||
{
|
||||
if (node)
|
||||
{
|
||||
plist_data_t data = plist_get_data(node);
|
||||
if (data)
|
||||
return data->type;
|
||||
}
|
||||
return PLIST_NONE;
|
||||
}
|
||||
|
||||
PLIST_API void plist_get_key_val(plist_t node, char **val)
|
||||
{
|
||||
plist_type type = plist_get_node_type(node);
|
||||
uint64_t length = 0;
|
||||
if (PLIST_KEY == type)
|
||||
plist_get_type_and_value(node, &type, (void *) val, &length);
|
||||
assert(length == strlen(*val));
|
||||
}
|
||||
|
||||
PLIST_API void plist_get_string_val(plist_t node, char **val)
|
||||
{
|
||||
plist_type type = plist_get_node_type(node);
|
||||
uint64_t length = 0;
|
||||
if (PLIST_STRING == type)
|
||||
plist_get_type_and_value(node, &type, (void *) val, &length);
|
||||
assert(length == strlen(*val));
|
||||
}
|
||||
|
||||
PLIST_API void plist_get_bool_val(plist_t node, uint8_t * val)
|
||||
{
|
||||
plist_type type = plist_get_node_type(node);
|
||||
uint64_t length = 0;
|
||||
if (PLIST_BOOLEAN == type)
|
||||
plist_get_type_and_value(node, &type, (void *) val, &length);
|
||||
assert(length == sizeof(uint8_t));
|
||||
}
|
||||
|
||||
PLIST_API void plist_get_uint_val(plist_t node, uint64_t * val)
|
||||
{
|
||||
plist_type type = plist_get_node_type(node);
|
||||
uint64_t length = 0;
|
||||
if (PLIST_UINT == type)
|
||||
plist_get_type_and_value(node, &type, (void *) val, &length);
|
||||
assert(length == sizeof(uint64_t) || length == 16);
|
||||
}
|
||||
|
||||
PLIST_API void plist_get_uid_val(plist_t node, uint64_t * val)
|
||||
{
|
||||
plist_type type = plist_get_node_type(node);
|
||||
uint64_t length = 0;
|
||||
if (PLIST_UID == type)
|
||||
plist_get_type_and_value(node, &type, (void *) val, &length);
|
||||
assert(length == sizeof(uint64_t));
|
||||
}
|
||||
|
||||
PLIST_API void plist_get_real_val(plist_t node, double *val)
|
||||
{
|
||||
plist_type type = plist_get_node_type(node);
|
||||
uint64_t length = 0;
|
||||
if (PLIST_REAL == type)
|
||||
plist_get_type_and_value(node, &type, (void *) val, &length);
|
||||
assert(length == sizeof(double));
|
||||
}
|
||||
|
||||
PLIST_API void plist_get_data_val(plist_t node, char **val, uint64_t * length)
|
||||
{
|
||||
plist_type type = plist_get_node_type(node);
|
||||
if (PLIST_DATA == type)
|
||||
plist_get_type_and_value(node, &type, (void *) val, length);
|
||||
}
|
||||
|
||||
PLIST_API void plist_get_date_val(plist_t node, int32_t * sec, int32_t * usec)
|
||||
{
|
||||
plist_type type = plist_get_node_type(node);
|
||||
uint64_t length = 0;
|
||||
double val = 0;
|
||||
if (PLIST_DATE == type)
|
||||
plist_get_type_and_value(node, &type, (void *) &val, &length);
|
||||
assert(length == sizeof(double));
|
||||
*sec = (int32_t)val;
|
||||
*usec = (int32_t)fabs((val - (int64_t)val) * 1000000);
|
||||
}
|
||||
|
||||
int plist_data_compare(const void *a, const void *b)
|
||||
{
|
||||
plist_data_t val_a = NULL;
|
||||
plist_data_t val_b = NULL;
|
||||
|
||||
if (!a || !b)
|
||||
return FALSE;
|
||||
|
||||
if (!((node_t*) a)->data || !((node_t*) b)->data)
|
||||
return FALSE;
|
||||
|
||||
val_a = plist_get_data((plist_t) a);
|
||||
val_b = plist_get_data((plist_t) b);
|
||||
|
||||
if (val_a->type != val_b->type)
|
||||
return FALSE;
|
||||
|
||||
switch (val_a->type)
|
||||
{
|
||||
case PLIST_BOOLEAN:
|
||||
case PLIST_UINT:
|
||||
case PLIST_REAL:
|
||||
case PLIST_DATE:
|
||||
case PLIST_UID:
|
||||
if (val_a->length != val_b->length)
|
||||
return FALSE;
|
||||
if (val_a->intval == val_b->intval) //it is an union so this is sufficient
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
|
||||
case PLIST_KEY:
|
||||
case PLIST_STRING:
|
||||
if (!strcmp(val_a->strval, val_b->strval))
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
|
||||
case PLIST_DATA:
|
||||
if (val_a->length != val_b->length)
|
||||
return FALSE;
|
||||
if (!memcmp(val_a->buff, val_b->buff, val_a->length))
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
case PLIST_ARRAY:
|
||||
case PLIST_DICT:
|
||||
//compare pointer
|
||||
if (a == b)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
PLIST_API char plist_compare_node_value(plist_t node_l, plist_t node_r)
|
||||
{
|
||||
return plist_data_compare(node_l, node_r);
|
||||
}
|
||||
|
||||
static void plist_set_element_val(plist_t node, plist_type type, const void *value, uint64_t length)
|
||||
{
|
||||
//free previous allocated buffer
|
||||
plist_data_t data = plist_get_data(node);
|
||||
assert(data); // a node should always have data attached
|
||||
|
||||
switch (data->type)
|
||||
{
|
||||
case PLIST_KEY:
|
||||
case PLIST_STRING:
|
||||
free(data->strval);
|
||||
data->strval = NULL;
|
||||
break;
|
||||
case PLIST_DATA:
|
||||
free(data->buff);
|
||||
data->buff = NULL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
//now handle value
|
||||
|
||||
data->type = type;
|
||||
data->length = length;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case PLIST_BOOLEAN:
|
||||
data->boolval = *((char *) value);
|
||||
break;
|
||||
case PLIST_UINT:
|
||||
case PLIST_UID:
|
||||
data->intval = *((uint64_t *) value);
|
||||
break;
|
||||
case PLIST_REAL:
|
||||
case PLIST_DATE:
|
||||
data->realval = *((double *) value);
|
||||
break;
|
||||
case PLIST_KEY:
|
||||
case PLIST_STRING:
|
||||
data->strval = strdup((char *) value);
|
||||
break;
|
||||
case PLIST_DATA:
|
||||
data->buff = (uint8_t *) malloc(length);
|
||||
memcpy(data->buff, value, length);
|
||||
break;
|
||||
case PLIST_ARRAY:
|
||||
case PLIST_DICT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PLIST_API void plist_set_key_val(plist_t node, const char *val)
|
||||
{
|
||||
plist_set_element_val(node, PLIST_KEY, val, strlen(val));
|
||||
}
|
||||
|
||||
PLIST_API void plist_set_string_val(plist_t node, const char *val)
|
||||
{
|
||||
plist_set_element_val(node, PLIST_STRING, val, strlen(val));
|
||||
}
|
||||
|
||||
PLIST_API void plist_set_bool_val(plist_t node, uint8_t val)
|
||||
{
|
||||
plist_set_element_val(node, PLIST_BOOLEAN, &val, sizeof(uint8_t));
|
||||
}
|
||||
|
||||
PLIST_API void plist_set_uint_val(plist_t node, uint64_t val)
|
||||
{
|
||||
plist_set_element_val(node, PLIST_UINT, &val, sizeof(uint64_t));
|
||||
}
|
||||
|
||||
PLIST_API void plist_set_uid_val(plist_t node, uint64_t val)
|
||||
{
|
||||
plist_set_element_val(node, PLIST_UID, &val, sizeof(uint64_t));
|
||||
}
|
||||
|
||||
PLIST_API void plist_set_real_val(plist_t node, double val)
|
||||
{
|
||||
plist_set_element_val(node, PLIST_REAL, &val, sizeof(double));
|
||||
}
|
||||
|
||||
PLIST_API void plist_set_data_val(plist_t node, const char *val, uint64_t length)
|
||||
{
|
||||
plist_set_element_val(node, PLIST_DATA, val, length);
|
||||
}
|
||||
|
||||
PLIST_API void plist_set_date_val(plist_t node, int32_t sec, int32_t usec)
|
||||
{
|
||||
double val = (double)sec + (double)usec / 1000000;
|
||||
plist_set_element_val(node, PLIST_DATE, &val, sizeof(struct timeval));
|
||||
}
|
||||
|
||||
74
lib/plist/plist.h
Executable file
74
lib/plist/plist.h
Executable file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* plist.h
|
||||
* contains structures and the like for plists
|
||||
*
|
||||
* Copyright (c) 2008 Zach C. All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef PLIST_H
|
||||
#define PLIST_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "plist/plist.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4996)
|
||||
#pragma warning(disable:4244)
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#define PLIST_API __declspec( dllexport )
|
||||
#else
|
||||
#ifdef HAVE_FVISIBILITY
|
||||
#define PLIST_API __attribute__((visibility("default")))
|
||||
#else
|
||||
#define PLIST_API
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct plist_data_s
|
||||
{
|
||||
union
|
||||
{
|
||||
char boolval;
|
||||
uint64_t intval;
|
||||
double realval;
|
||||
char *strval;
|
||||
uint8_t *buff;
|
||||
void *hashtable;
|
||||
};
|
||||
uint64_t length;
|
||||
plist_type type;
|
||||
};
|
||||
|
||||
typedef struct plist_data_s *plist_data_t;
|
||||
|
||||
plist_t plist_new_node(plist_data_t data);
|
||||
plist_data_t plist_get_data(const plist_t node);
|
||||
plist_data_t plist_new_plist_data(void);
|
||||
void plist_free_data(plist_data_t data);
|
||||
int plist_data_compare(const void *a, const void *b);
|
||||
|
||||
|
||||
#endif
|
||||
686
lib/plist/plist/plist.h
Executable file
686
lib/plist/plist/plist.h
Executable file
@@ -0,0 +1,686 @@
|
||||
/**
|
||||
* @file plist/plist.h
|
||||
* @brief Main include of libplist
|
||||
* \internal
|
||||
*
|
||||
* Copyright (c) 2008 Jonathan Beck All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef LIBPLIST_H
|
||||
#define LIBPLIST_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
typedef __int8 int8_t;
|
||||
typedef __int16 int16_t;
|
||||
typedef __int32 int32_t;
|
||||
typedef __int64 int64_t;
|
||||
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#ifdef __llvm__
|
||||
#if defined(__has_extension)
|
||||
#if (__has_extension(attribute_deprecated_with_message))
|
||||
#ifndef PLIST_WARN_DEPRECATED
|
||||
#define PLIST_WARN_DEPRECATED(x) __attribute__((deprecated(x)))
|
||||
#endif
|
||||
#else
|
||||
#ifndef PLIST_WARN_DEPRECATED
|
||||
#define PLIST_WARN_DEPRECATED(x) __attribute__((deprecated))
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
#ifndef PLIST_WARN_DEPRECATED
|
||||
#define PLIST_WARN_DEPRECATED(x) __attribute__((deprecated))
|
||||
#endif
|
||||
#endif
|
||||
#elif (__GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 5)))
|
||||
#ifndef PLIST_WARN_DEPRECATED
|
||||
#define PLIST_WARN_DEPRECATED(x) __attribute__((deprecated(x)))
|
||||
#endif
|
||||
#elif defined(_MSC_VER)
|
||||
#ifndef PLIST_WARN_DEPRECATED
|
||||
#define PLIST_WARN_DEPRECATED(x) __declspec(deprecated(x))
|
||||
#endif
|
||||
#else
|
||||
#define PLIST_WARN_DEPRECATED(x)
|
||||
#pragma message("WARNING: You need to implement DEPRECATED for this compiler")
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/**
|
||||
* \mainpage libplist : A library to handle Apple Property Lists
|
||||
* \defgroup PublicAPI Public libplist API
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
|
||||
/**
|
||||
* The basic plist abstract data type.
|
||||
*/
|
||||
typedef void *plist_t;
|
||||
|
||||
/**
|
||||
* The plist dictionary iterator.
|
||||
*/
|
||||
typedef void *plist_dict_iter;
|
||||
|
||||
/**
|
||||
* The enumeration of plist node types.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
PLIST_BOOLEAN, /**< Boolean, scalar type */
|
||||
PLIST_UINT, /**< Unsigned integer, scalar type */
|
||||
PLIST_REAL, /**< Real, scalar type */
|
||||
PLIST_STRING, /**< ASCII string, scalar type */
|
||||
PLIST_ARRAY, /**< Ordered array, structured type */
|
||||
PLIST_DICT, /**< Unordered dictionary (key/value pair), structured type */
|
||||
PLIST_DATE, /**< Date, scalar type */
|
||||
PLIST_DATA, /**< Binary data, scalar type */
|
||||
PLIST_KEY, /**< Key in dictionaries (ASCII String), scalar type */
|
||||
PLIST_UID, /**< Special type used for 'keyed encoding' */
|
||||
PLIST_NONE /**< No type */
|
||||
} plist_type;
|
||||
|
||||
|
||||
/********************************************
|
||||
* *
|
||||
* Creation & Destruction *
|
||||
* *
|
||||
********************************************/
|
||||
|
||||
/**
|
||||
* Create a new root plist_t type #PLIST_DICT
|
||||
*
|
||||
* @return the created plist
|
||||
* @sa #plist_type
|
||||
*/
|
||||
plist_t plist_new_dict(void);
|
||||
|
||||
/**
|
||||
* Create a new root plist_t type #PLIST_ARRAY
|
||||
*
|
||||
* @return the created plist
|
||||
* @sa #plist_type
|
||||
*/
|
||||
plist_t plist_new_array(void);
|
||||
|
||||
/**
|
||||
* Create a new plist_t type #PLIST_STRING
|
||||
*
|
||||
* @param val the sting value, encoded in UTF8.
|
||||
* @return the created item
|
||||
* @sa #plist_type
|
||||
*/
|
||||
plist_t plist_new_string(const char *val);
|
||||
|
||||
/**
|
||||
* Create a new plist_t type #PLIST_BOOLEAN
|
||||
*
|
||||
* @param val the boolean value, 0 is false, other values are true.
|
||||
* @return the created item
|
||||
* @sa #plist_type
|
||||
*/
|
||||
plist_t plist_new_bool(uint8_t val);
|
||||
|
||||
/**
|
||||
* Create a new plist_t type #PLIST_UINT
|
||||
*
|
||||
* @param val the unsigned integer value
|
||||
* @return the created item
|
||||
* @sa #plist_type
|
||||
*/
|
||||
plist_t plist_new_uint(uint64_t val);
|
||||
|
||||
/**
|
||||
* Create a new plist_t type #PLIST_REAL
|
||||
*
|
||||
* @param val the real value
|
||||
* @return the created item
|
||||
* @sa #plist_type
|
||||
*/
|
||||
plist_t plist_new_real(double val);
|
||||
|
||||
/**
|
||||
* Create a new plist_t type #PLIST_DATA
|
||||
*
|
||||
* @param val the binary buffer
|
||||
* @param length the length of the buffer
|
||||
* @return the created item
|
||||
* @sa #plist_type
|
||||
*/
|
||||
plist_t plist_new_data(const char *val, uint64_t length);
|
||||
|
||||
/**
|
||||
* Create a new plist_t type #PLIST_DATE
|
||||
*
|
||||
* @param sec the number of seconds since 01/01/2001
|
||||
* @param usec the number of microseconds
|
||||
* @return the created item
|
||||
* @sa #plist_type
|
||||
*/
|
||||
plist_t plist_new_date(int32_t sec, int32_t usec);
|
||||
|
||||
/**
|
||||
* Create a new plist_t type #PLIST_UID
|
||||
*
|
||||
* @param val the unsigned integer value
|
||||
* @return the created item
|
||||
* @sa #plist_type
|
||||
*/
|
||||
plist_t plist_new_uid(uint64_t val);
|
||||
|
||||
/**
|
||||
* Destruct a plist_t node and all its children recursively
|
||||
*
|
||||
* @param plist the plist to free
|
||||
*/
|
||||
void plist_free(plist_t plist);
|
||||
|
||||
/**
|
||||
* Return a copy of passed node and it's children
|
||||
*
|
||||
* @param node the plist to copy
|
||||
* @return copied plist
|
||||
*/
|
||||
plist_t plist_copy(plist_t node);
|
||||
|
||||
|
||||
/********************************************
|
||||
* *
|
||||
* Array functions *
|
||||
* *
|
||||
********************************************/
|
||||
|
||||
/**
|
||||
* Get size of a #PLIST_ARRAY node.
|
||||
*
|
||||
* @param node the node of type #PLIST_ARRAY
|
||||
* @return size of the #PLIST_ARRAY node
|
||||
*/
|
||||
uint32_t plist_array_get_size(plist_t node);
|
||||
|
||||
/**
|
||||
* Get the nth item in a #PLIST_ARRAY node.
|
||||
*
|
||||
* @param node the node of type #PLIST_ARRAY
|
||||
* @param n the index of the item to get. Range is [0, array_size[
|
||||
* @return the nth item or NULL if node is not of type #PLIST_ARRAY
|
||||
*/
|
||||
plist_t plist_array_get_item(plist_t node, uint32_t n);
|
||||
|
||||
/**
|
||||
* Get the index of an item. item must be a member of a #PLIST_ARRAY node.
|
||||
*
|
||||
* @param node the node
|
||||
* @return the node index
|
||||
*/
|
||||
uint32_t plist_array_get_item_index(plist_t node);
|
||||
|
||||
/**
|
||||
* Set the nth item in a #PLIST_ARRAY node.
|
||||
* The previous item at index n will be freed using #plist_free
|
||||
*
|
||||
* @param node the node of type #PLIST_ARRAY
|
||||
* @param item the new item at index n. The array is responsible for freeing item when it is no longer needed.
|
||||
* @param n the index of the item to get. Range is [0, array_size[. Assert if n is not in range.
|
||||
*/
|
||||
void plist_array_set_item(plist_t node, plist_t item, uint32_t n);
|
||||
|
||||
/**
|
||||
* Append a new item at the end of a #PLIST_ARRAY node.
|
||||
*
|
||||
* @param node the node of type #PLIST_ARRAY
|
||||
* @param item the new item. The array is responsible for freeing item when it is no longer needed.
|
||||
*/
|
||||
void plist_array_append_item(plist_t node, plist_t item);
|
||||
|
||||
/**
|
||||
* Insert a new item at position n in a #PLIST_ARRAY node.
|
||||
*
|
||||
* @param node the node of type #PLIST_ARRAY
|
||||
* @param item the new item to insert. The array is responsible for freeing item when it is no longer needed.
|
||||
* @param n The position at which the node will be stored. Range is [0, array_size[. Assert if n is not in range.
|
||||
*/
|
||||
void plist_array_insert_item(plist_t node, plist_t item, uint32_t n);
|
||||
|
||||
/**
|
||||
* Remove an existing position in a #PLIST_ARRAY node.
|
||||
* Removed position will be freed using #plist_free.
|
||||
*
|
||||
* @param node the node of type #PLIST_ARRAY
|
||||
* @param n The position to remove. Range is [0, array_size[. Assert if n is not in range.
|
||||
*/
|
||||
void plist_array_remove_item(plist_t node, uint32_t n);
|
||||
|
||||
/********************************************
|
||||
* *
|
||||
* Dictionary functions *
|
||||
* *
|
||||
********************************************/
|
||||
|
||||
/**
|
||||
* Get size of a #PLIST_DICT node.
|
||||
*
|
||||
* @param node the node of type #PLIST_DICT
|
||||
* @return size of the #PLIST_DICT node
|
||||
*/
|
||||
uint32_t plist_dict_get_size(plist_t node);
|
||||
|
||||
/**
|
||||
* Create an iterator of a #PLIST_DICT node.
|
||||
* The allocated iterator should be freed with the standard free function.
|
||||
*
|
||||
* @param node the node of type #PLIST_DICT
|
||||
* @param iter iterator of the #PLIST_DICT node
|
||||
*/
|
||||
void plist_dict_new_iter(plist_t node, plist_dict_iter *iter);
|
||||
|
||||
/**
|
||||
* Increment iterator of a #PLIST_DICT node.
|
||||
*
|
||||
* @param node the node of type #PLIST_DICT
|
||||
* @param iter iterator of the dictionary
|
||||
* @param key a location to store the key, or NULL. The caller is responsible
|
||||
* for freeing the the returned string.
|
||||
* @param val a location to store the value, or NULL. The caller should *not*
|
||||
* free the returned value.
|
||||
*/
|
||||
void plist_dict_next_item(plist_t node, plist_dict_iter iter, char **key, plist_t *val);
|
||||
|
||||
/**
|
||||
* Get key associated to an item. Item must be member of a dictionary
|
||||
*
|
||||
* @param node the node
|
||||
* @param key a location to store the key. The caller is responsible for freeing the returned string.
|
||||
*/
|
||||
void plist_dict_get_item_key(plist_t node, char **key);
|
||||
|
||||
/**
|
||||
* Get the nth item in a #PLIST_DICT node.
|
||||
*
|
||||
* @param node the node of type #PLIST_DICT
|
||||
* @param key the identifier of the item to get.
|
||||
* @return the item or NULL if node is not of type #PLIST_DICT. The caller should not free
|
||||
* the returned node.
|
||||
*/
|
||||
plist_t plist_dict_get_item(plist_t node, const char* key);
|
||||
|
||||
/**
|
||||
* Set item identified by key in a #PLIST_DICT node.
|
||||
* The previous item identified by key will be freed using #plist_free.
|
||||
* If there is no item for the given key a new item will be inserted.
|
||||
*
|
||||
* @param node the node of type #PLIST_DICT
|
||||
* @param item the new item associated to key
|
||||
* @param key the identifier of the item to set.
|
||||
*/
|
||||
void plist_dict_set_item(plist_t node, const char* key, plist_t item);
|
||||
|
||||
/**
|
||||
* Insert a new item into a #PLIST_DICT node.
|
||||
*
|
||||
* @deprecated Deprecated. Use plist_dict_set_item instead.
|
||||
*
|
||||
* @param node the node of type #PLIST_DICT
|
||||
* @param item the new item to insert
|
||||
* @param key The identifier of the item to insert.
|
||||
*/
|
||||
PLIST_WARN_DEPRECATED("use plist_dict_set_item instead")
|
||||
void plist_dict_insert_item(plist_t node, const char* key, plist_t item);
|
||||
|
||||
/**
|
||||
* Remove an existing position in a #PLIST_DICT node.
|
||||
* Removed position will be freed using #plist_free
|
||||
*
|
||||
* @param node the node of type #PLIST_DICT
|
||||
* @param key The identifier of the item to remove. Assert if identifier is not present.
|
||||
*/
|
||||
void plist_dict_remove_item(plist_t node, const char* key);
|
||||
|
||||
/**
|
||||
* Merge a dictionary into another. This will add all key/value pairs
|
||||
* from the source dictionary to the target dictionary, overwriting
|
||||
* any existing key/value pairs that are already present in target.
|
||||
*
|
||||
* @param target pointer to an existing node of type #PLIST_DICT
|
||||
* @param source node of type #PLIST_DICT that should be merged into target
|
||||
*/
|
||||
void plist_dict_merge(plist_t *target, plist_t source);
|
||||
|
||||
|
||||
/********************************************
|
||||
* *
|
||||
* Getters *
|
||||
* *
|
||||
********************************************/
|
||||
|
||||
/**
|
||||
* Get the parent of a node
|
||||
*
|
||||
* @param node the parent (NULL if node is root)
|
||||
*/
|
||||
plist_t plist_get_parent(plist_t node);
|
||||
|
||||
/**
|
||||
* Get the #plist_type of a node.
|
||||
*
|
||||
* @param node the node
|
||||
* @return the type of the node
|
||||
*/
|
||||
plist_type plist_get_node_type(plist_t node);
|
||||
|
||||
/**
|
||||
* Get the value of a #PLIST_KEY node.
|
||||
* This function does nothing if node is not of type #PLIST_KEY
|
||||
*
|
||||
* @param node the node
|
||||
* @param val a pointer to a C-string. This function allocates the memory,
|
||||
* caller is responsible for freeing it.
|
||||
*/
|
||||
void plist_get_key_val(plist_t node, char **val);
|
||||
|
||||
/**
|
||||
* Get the value of a #PLIST_STRING node.
|
||||
* This function does nothing if node is not of type #PLIST_STRING
|
||||
*
|
||||
* @param node the node
|
||||
* @param val a pointer to a C-string. This function allocates the memory,
|
||||
* caller is responsible for freeing it. Data is UTF-8 encoded.
|
||||
*/
|
||||
void plist_get_string_val(plist_t node, char **val);
|
||||
|
||||
/**
|
||||
* Get the value of a #PLIST_BOOLEAN node.
|
||||
* This function does nothing if node is not of type #PLIST_BOOLEAN
|
||||
*
|
||||
* @param node the node
|
||||
* @param val a pointer to a uint8_t variable.
|
||||
*/
|
||||
void plist_get_bool_val(plist_t node, uint8_t * val);
|
||||
|
||||
/**
|
||||
* Get the value of a #PLIST_UINT node.
|
||||
* This function does nothing if node is not of type #PLIST_UINT
|
||||
*
|
||||
* @param node the node
|
||||
* @param val a pointer to a uint64_t variable.
|
||||
*/
|
||||
void plist_get_uint_val(plist_t node, uint64_t * val);
|
||||
|
||||
/**
|
||||
* Get the value of a #PLIST_REAL node.
|
||||
* This function does nothing if node is not of type #PLIST_REAL
|
||||
*
|
||||
* @param node the node
|
||||
* @param val a pointer to a double variable.
|
||||
*/
|
||||
void plist_get_real_val(plist_t node, double *val);
|
||||
|
||||
/**
|
||||
* Get the value of a #PLIST_DATA node.
|
||||
* This function does nothing if node is not of type #PLIST_DATA
|
||||
*
|
||||
* @param node the node
|
||||
* @param val a pointer to an unallocated char buffer. This function allocates the memory,
|
||||
* caller is responsible for freeing it.
|
||||
* @param length the length of the buffer
|
||||
*/
|
||||
void plist_get_data_val(plist_t node, char **val, uint64_t * length);
|
||||
|
||||
/**
|
||||
* Get the value of a #PLIST_DATE node.
|
||||
* This function does nothing if node is not of type #PLIST_DATE
|
||||
*
|
||||
* @param node the node
|
||||
* @param sec a pointer to an int32_t variable. Represents the number of seconds since 01/01/2001.
|
||||
* @param usec a pointer to an int32_t variable. Represents the number of microseconds
|
||||
*/
|
||||
void plist_get_date_val(plist_t node, int32_t * sec, int32_t * usec);
|
||||
|
||||
/**
|
||||
* Get the value of a #PLIST_UID node.
|
||||
* This function does nothing if node is not of type #PLIST_UID
|
||||
*
|
||||
* @param node the node
|
||||
* @param val a pointer to a uint64_t variable.
|
||||
*/
|
||||
void plist_get_uid_val(plist_t node, uint64_t * val);
|
||||
|
||||
|
||||
/********************************************
|
||||
* *
|
||||
* Setters *
|
||||
* *
|
||||
********************************************/
|
||||
|
||||
/**
|
||||
* Set the value of a node.
|
||||
* Forces type of node to #PLIST_KEY
|
||||
*
|
||||
* @param node the node
|
||||
* @param val the key value
|
||||
*/
|
||||
void plist_set_key_val(plist_t node, const char *val);
|
||||
|
||||
/**
|
||||
* Set the value of a node.
|
||||
* Forces type of node to #PLIST_STRING
|
||||
*
|
||||
* @param node the node
|
||||
* @param val the string value. The string is copied when set and will be
|
||||
* freed by the node.
|
||||
*/
|
||||
void plist_set_string_val(plist_t node, const char *val);
|
||||
|
||||
/**
|
||||
* Set the value of a node.
|
||||
* Forces type of node to #PLIST_BOOLEAN
|
||||
*
|
||||
* @param node the node
|
||||
* @param val the boolean value
|
||||
*/
|
||||
void plist_set_bool_val(plist_t node, uint8_t val);
|
||||
|
||||
/**
|
||||
* Set the value of a node.
|
||||
* Forces type of node to #PLIST_UINT
|
||||
*
|
||||
* @param node the node
|
||||
* @param val the unsigned integer value
|
||||
*/
|
||||
void plist_set_uint_val(plist_t node, uint64_t val);
|
||||
|
||||
/**
|
||||
* Set the value of a node.
|
||||
* Forces type of node to #PLIST_REAL
|
||||
*
|
||||
* @param node the node
|
||||
* @param val the real value
|
||||
*/
|
||||
void plist_set_real_val(plist_t node, double val);
|
||||
|
||||
/**
|
||||
* Set the value of a node.
|
||||
* Forces type of node to #PLIST_DATA
|
||||
*
|
||||
* @param node the node
|
||||
* @param val the binary buffer. The buffer is copied when set and will
|
||||
* be freed by the node.
|
||||
* @param length the length of the buffer
|
||||
*/
|
||||
void plist_set_data_val(plist_t node, const char *val, uint64_t length);
|
||||
|
||||
/**
|
||||
* Set the value of a node.
|
||||
* Forces type of node to #PLIST_DATE
|
||||
*
|
||||
* @param node the node
|
||||
* @param sec the number of seconds since 01/01/2001
|
||||
* @param usec the number of microseconds
|
||||
*/
|
||||
void plist_set_date_val(plist_t node, int32_t sec, int32_t usec);
|
||||
|
||||
/**
|
||||
* Set the value of a node.
|
||||
* Forces type of node to #PLIST_UID
|
||||
*
|
||||
* @param node the node
|
||||
* @param val the unsigned integer value
|
||||
*/
|
||||
void plist_set_uid_val(plist_t node, uint64_t val);
|
||||
|
||||
|
||||
/********************************************
|
||||
* *
|
||||
* Import & Export *
|
||||
* *
|
||||
********************************************/
|
||||
|
||||
/**
|
||||
* Export the #plist_t structure to XML format.
|
||||
*
|
||||
* @param plist the root node to export
|
||||
* @param plist_xml a pointer to a C-string. This function allocates the memory,
|
||||
* caller is responsible for freeing it. Data is UTF-8 encoded.
|
||||
* @param length a pointer to an uint32_t variable. Represents the length of the allocated buffer.
|
||||
*/
|
||||
void plist_to_xml(plist_t plist, char **plist_xml, uint32_t * length);
|
||||
|
||||
/**
|
||||
* Export the #plist_t structure to binary format.
|
||||
*
|
||||
* @param plist the root node to export
|
||||
* @param plist_bin a pointer to a char* buffer. This function allocates the memory,
|
||||
* caller is responsible for freeing it.
|
||||
* @param length a pointer to an uint32_t variable. Represents the length of the allocated buffer.
|
||||
*/
|
||||
void plist_to_bin(plist_t plist, char **plist_bin, uint32_t * length);
|
||||
|
||||
/**
|
||||
* Import the #plist_t structure from XML format.
|
||||
*
|
||||
* @param plist_xml a pointer to the xml buffer.
|
||||
* @param length length of the buffer to read.
|
||||
* @param plist a pointer to the imported plist.
|
||||
*/
|
||||
void plist_from_xml(const char *plist_xml, uint32_t length, plist_t * plist);
|
||||
|
||||
/**
|
||||
* Import the #plist_t structure from binary format.
|
||||
*
|
||||
* @param plist_bin a pointer to the xml buffer.
|
||||
* @param length length of the buffer to read.
|
||||
* @param plist a pointer to the imported plist.
|
||||
*/
|
||||
void plist_from_bin(const char *plist_bin, uint32_t length, plist_t * plist);
|
||||
|
||||
/**
|
||||
* Import the #plist_t structure from memory data.
|
||||
* This method will look at the first bytes of plist_data
|
||||
* to determine if plist_data contains a binary or XML plist.
|
||||
*
|
||||
* @param plist_data a pointer to the memory buffer containing plist data.
|
||||
* @param length length of the buffer to read.
|
||||
* @param plist a pointer to the imported plist.
|
||||
*/
|
||||
void plist_from_memory(const char *plist_data, uint32_t length, plist_t * plist);
|
||||
|
||||
/**
|
||||
* Test if in-memory plist data is binary or XML
|
||||
* This method will look at the first bytes of plist_data
|
||||
* to determine if plist_data contains a binary or XML plist.
|
||||
* This method is not validating the whole memory buffer to check if the
|
||||
* content is truly a plist, it's only using some heuristic on the first few
|
||||
* bytes of plist_data.
|
||||
*
|
||||
* @param plist_data a pointer to the memory buffer containing plist data.
|
||||
* @param length length of the buffer to read.
|
||||
* @return 1 if the buffer is a binary plist, 0 otherwise.
|
||||
*/
|
||||
int plist_is_binary(const char *plist_data, uint32_t length);
|
||||
|
||||
/********************************************
|
||||
* *
|
||||
* Utils *
|
||||
* *
|
||||
********************************************/
|
||||
|
||||
/**
|
||||
* Get a node from its path. Each path element depends on the associated father node type.
|
||||
* For Dictionaries, var args are casted to const char*, for arrays, var args are caster to uint32_t
|
||||
* Search is breath first order.
|
||||
*
|
||||
* @param plist the node to access result from.
|
||||
* @param length length of the path to access
|
||||
* @return the value to access.
|
||||
*/
|
||||
plist_t plist_access_path(plist_t plist, uint32_t length, ...);
|
||||
|
||||
/**
|
||||
* Variadic version of #plist_access_path.
|
||||
*
|
||||
* @param plist the node to access result from.
|
||||
* @param length length of the path to access
|
||||
* @param v list of array's index and dic'st key
|
||||
* @return the value to access.
|
||||
*/
|
||||
plist_t plist_access_pathv(plist_t plist, uint32_t length, va_list v);
|
||||
|
||||
/**
|
||||
* Compare two node values
|
||||
*
|
||||
* @param node_l left node to compare
|
||||
* @param node_r rigth node to compare
|
||||
* @return TRUE is type and value match, FALSE otherwise.
|
||||
*/
|
||||
char plist_compare_node_value(plist_t node_l, plist_t node_r);
|
||||
|
||||
#define _PLIST_IS_TYPE(__plist, __plist_type) (__plist && (plist_get_node_type(__plist) == PLIST_##__plist_type))
|
||||
|
||||
/* Helper macros for the different plist types */
|
||||
#define PLIST_IS_BOOLEAN(__plist) _PLIST_IS_TYPE(__plist, BOOLEAN)
|
||||
#define PLIST_IS_UINT(__plist) _PLIST_IS_TYPE(__plist, UINT)
|
||||
#define PLIST_IS_REAL(__plist) _PLIST_IS_TYPE(__plist, REAL)
|
||||
#define PLIST_IS_STRING(__plist) _PLIST_IS_TYPE(__plist, STRING)
|
||||
#define PLIST_IS_ARRAY(__plist) _PLIST_IS_TYPE(__plist, ARRAY)
|
||||
#define PLIST_IS_DICT(__plist) _PLIST_IS_TYPE(__plist, DICT)
|
||||
#define PLIST_IS_DATE(__plist) _PLIST_IS_TYPE(__plist, DATE)
|
||||
#define PLIST_IS_DATA(__plist) _PLIST_IS_TYPE(__plist, DATA)
|
||||
#define PLIST_IS_KEY(__plist) _PLIST_IS_TYPE(__plist, KEY)
|
||||
#define PLIST_IS_UID(__plist) _PLIST_IS_TYPE(__plist, UID)
|
||||
|
||||
/*@}*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
61
lib/plist/ptrarray.c
Executable file
61
lib/plist/ptrarray.c
Executable file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* ptrarray.c
|
||||
* simple pointer array implementation
|
||||
*
|
||||
* Copyright (c) 2011 Nikias Bassen, All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include "ptrarray.h"
|
||||
|
||||
ptrarray_t *ptr_array_new(int capacity)
|
||||
{
|
||||
ptrarray_t *pa = (ptrarray_t*)malloc(sizeof(ptrarray_t));
|
||||
pa->pdata = (void**)malloc(sizeof(void*) * capacity);
|
||||
pa->capacity = capacity;
|
||||
pa->capacity_step = (capacity > 4096) ? 4096 : capacity;
|
||||
pa->len = 0;
|
||||
return pa;
|
||||
}
|
||||
|
||||
void ptr_array_free(ptrarray_t *pa)
|
||||
{
|
||||
if (!pa) return;
|
||||
if (pa->pdata) {
|
||||
free(pa->pdata);
|
||||
}
|
||||
free(pa);
|
||||
}
|
||||
|
||||
void ptr_array_add(ptrarray_t *pa, void *data)
|
||||
{
|
||||
if (!pa || !pa->pdata || !data) return;
|
||||
size_t remaining = pa->capacity-pa->len;
|
||||
if (remaining == 0) {
|
||||
pa->pdata = realloc(pa->pdata, sizeof(void*) * (pa->capacity + pa->capacity_step));
|
||||
pa->capacity += pa->capacity_step;
|
||||
}
|
||||
pa->pdata[pa->len] = data;
|
||||
pa->len++;
|
||||
}
|
||||
|
||||
void* ptr_array_index(ptrarray_t *pa, size_t array_index)
|
||||
{
|
||||
if (!pa) return NULL;
|
||||
if (array_index >= pa->len) {
|
||||
return NULL;
|
||||
}
|
||||
return pa->pdata[array_index];
|
||||
}
|
||||
36
lib/plist/ptrarray.h
Executable file
36
lib/plist/ptrarray.h
Executable file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* ptrarray.h
|
||||
* header file for simple pointer array implementation
|
||||
*
|
||||
* Copyright (c) 2011 Nikias Bassen, All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef PTRARRAY_H
|
||||
#define PTRARRAY_H
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct ptrarray_t {
|
||||
void **pdata;
|
||||
size_t len;
|
||||
size_t capacity;
|
||||
size_t capacity_step;
|
||||
} ptrarray_t;
|
||||
|
||||
ptrarray_t *ptr_array_new(int capacity);
|
||||
void ptr_array_free(ptrarray_t *pa);
|
||||
void ptr_array_add(ptrarray_t *pa, void *data);
|
||||
void* ptr_array_index(ptrarray_t *pa, size_t index);
|
||||
#endif
|
||||
34
lib/plist/strbuf.h
Executable file
34
lib/plist/strbuf.h
Executable file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* strbuf.h
|
||||
* header file for simple string buffer, using the bytearray as underlying
|
||||
* structure.
|
||||
*
|
||||
* Copyright (c) 2016 Nikias Bassen, All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef STRBUF_H
|
||||
#define STRBUF_H
|
||||
#include <stdlib.h>
|
||||
#include "bytearray.h"
|
||||
|
||||
typedef struct bytearray_t strbuf_t;
|
||||
|
||||
#define str_buf_new(__sz) byte_array_new(__sz)
|
||||
#define str_buf_free(__ba) byte_array_free(__ba)
|
||||
#define str_buf_grow(__ba, __am) byte_array_grow(__ba, __am)
|
||||
#define str_buf_append(__ba, __str, __len) byte_array_append(__ba, (void*)(__str), __len)
|
||||
|
||||
#endif
|
||||
812
lib/plist/time64.c
Executable file
812
lib/plist/time64.c
Executable file
@@ -0,0 +1,812 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2007-2010 Michael G Schwern
|
||||
|
||||
This software originally derived from Paul Sheer's pivotal_gmtime_r.c.
|
||||
|
||||
The MIT License:
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
Programmers who have available to them 64-bit time values as a 'long
|
||||
long' type can use localtime64_r() and gmtime64_r() which correctly
|
||||
converts the time even on 32-bit systems. Whether you have 64-bit time
|
||||
values will depend on the operating system.
|
||||
|
||||
localtime64_r() is a 64-bit equivalent of localtime_r().
|
||||
|
||||
gmtime64_r() is a 64-bit equivalent of gmtime_r().
|
||||
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include "time64.h"
|
||||
#include "time64_limits.h"
|
||||
|
||||
|
||||
static const char days_in_month[2][12] = {
|
||||
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
|
||||
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
|
||||
};
|
||||
|
||||
static const short julian_days_by_month[2][12] = {
|
||||
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
|
||||
{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335},
|
||||
};
|
||||
|
||||
static char wday_name[7][4] = {
|
||||
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
|
||||
};
|
||||
|
||||
static char mon_name[12][4] = {
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
||||
};
|
||||
|
||||
static const short length_of_year[2] = { 365, 366 };
|
||||
|
||||
/* Some numbers relating to the gregorian cycle */
|
||||
static const Year years_in_gregorian_cycle = 400;
|
||||
#define days_in_gregorian_cycle ((365 * 400) + 100 - 4 + 1)
|
||||
static const Time64_T seconds_in_gregorian_cycle = days_in_gregorian_cycle * 60LL * 60LL * 24LL;
|
||||
|
||||
/* Year range we can trust the time funcitons with */
|
||||
#define MAX_SAFE_YEAR 2037
|
||||
#define MIN_SAFE_YEAR 1971
|
||||
|
||||
/* 28 year Julian calendar cycle */
|
||||
#define SOLAR_CYCLE_LENGTH 28
|
||||
|
||||
/* Year cycle from MAX_SAFE_YEAR down. */
|
||||
static const short safe_years_high[SOLAR_CYCLE_LENGTH] = {
|
||||
2016, 2017, 2018, 2019,
|
||||
2020, 2021, 2022, 2023,
|
||||
2024, 2025, 2026, 2027,
|
||||
2028, 2029, 2030, 2031,
|
||||
2032, 2033, 2034, 2035,
|
||||
2036, 2037, 2010, 2011,
|
||||
2012, 2013, 2014, 2015
|
||||
};
|
||||
|
||||
/* Year cycle from MIN_SAFE_YEAR up */
|
||||
static const int safe_years_low[SOLAR_CYCLE_LENGTH] = {
|
||||
1996, 1997, 1998, 1971,
|
||||
1972, 1973, 1974, 1975,
|
||||
1976, 1977, 1978, 1979,
|
||||
1980, 1981, 1982, 1983,
|
||||
1984, 1985, 1986, 1987,
|
||||
1988, 1989, 1990, 1991,
|
||||
1992, 1993, 1994, 1995,
|
||||
};
|
||||
|
||||
/* This isn't used, but it's handy to look at */
|
||||
#if 0
|
||||
static const char dow_year_start[SOLAR_CYCLE_LENGTH] = {
|
||||
5, 0, 1, 2, /* 0 2016 - 2019 */
|
||||
3, 5, 6, 0, /* 4 */
|
||||
1, 3, 4, 5, /* 8 1996 - 1998, 1971*/
|
||||
6, 1, 2, 3, /* 12 1972 - 1975 */
|
||||
4, 6, 0, 1, /* 16 */
|
||||
2, 4, 5, 6, /* 20 2036, 2037, 2010, 2011 */
|
||||
0, 2, 3, 4 /* 24 2012, 2013, 2014, 2015 */
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Let's assume people are going to be looking for dates in the future.
|
||||
Let's provide some cheats so you can skip ahead.
|
||||
This has a 4x speed boost when near 2008.
|
||||
*/
|
||||
/* Number of days since epoch on Jan 1st, 2008 GMT */
|
||||
#define CHEAT_DAYS (1199145600 / 24 / 60 / 60)
|
||||
#define CHEAT_YEARS 108
|
||||
|
||||
#define IS_LEAP(n) ((!(((n) + 1900) % 400) || (!(((n) + 1900) % 4) && (((n) + 1900) % 100))) != 0)
|
||||
#define WRAP(a,b,m) ((a) = ((a) < 0 ) ? ((b)--, (a) + (m)) : (a))
|
||||
|
||||
#ifdef USE_SYSTEM_LOCALTIME
|
||||
# define SHOULD_USE_SYSTEM_LOCALTIME(a) ( \
|
||||
(a) <= SYSTEM_LOCALTIME_MAX && \
|
||||
(a) >= SYSTEM_LOCALTIME_MIN \
|
||||
)
|
||||
#else
|
||||
# define SHOULD_USE_SYSTEM_LOCALTIME(a) (0)
|
||||
#endif
|
||||
|
||||
#ifdef USE_SYSTEM_GMTIME
|
||||
# define SHOULD_USE_SYSTEM_GMTIME(a) ( \
|
||||
(a) <= SYSTEM_GMTIME_MAX && \
|
||||
(a) >= SYSTEM_GMTIME_MIN \
|
||||
)
|
||||
#else
|
||||
# define SHOULD_USE_SYSTEM_GMTIME(a) (0)
|
||||
#endif
|
||||
|
||||
/* Multi varadic macros are a C99 thing, alas */
|
||||
#ifdef TIME_64_DEBUG
|
||||
# define TIME64_TRACE(format) (fprintf(stderr, format))
|
||||
# define TIME64_TRACE1(format, var1) (fprintf(stderr, format, var1))
|
||||
# define TIME64_TRACE2(format, var1, var2) (fprintf(stderr, format, var1, var2))
|
||||
# define TIME64_TRACE3(format, var1, var2, var3) (fprintf(stderr, format, var1, var2, var3))
|
||||
#else
|
||||
# define TIME64_TRACE(format) ((void)0)
|
||||
# define TIME64_TRACE1(format, var1) ((void)0)
|
||||
# define TIME64_TRACE2(format, var1, var2) ((void)0)
|
||||
# define TIME64_TRACE3(format, var1, var2, var3) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
static int is_exception_century(Year year)
|
||||
{
|
||||
int is_exception = ((year % 100 == 0) && !(year % 400 == 0));
|
||||
TIME64_TRACE1("# is_exception_century: %s\n", is_exception ? "yes" : "no");
|
||||
|
||||
return(is_exception);
|
||||
}
|
||||
|
||||
|
||||
/* Compare two dates.
|
||||
The result is like cmp.
|
||||
Ignores things like gmtoffset and dst
|
||||
*/
|
||||
static int cmp_date( const struct TM* left, const struct tm* right ) {
|
||||
if( left->tm_year > right->tm_year )
|
||||
return 1;
|
||||
else if( left->tm_year < right->tm_year )
|
||||
return -1;
|
||||
|
||||
if( left->tm_mon > right->tm_mon )
|
||||
return 1;
|
||||
else if( left->tm_mon < right->tm_mon )
|
||||
return -1;
|
||||
|
||||
if( left->tm_mday > right->tm_mday )
|
||||
return 1;
|
||||
else if( left->tm_mday < right->tm_mday )
|
||||
return -1;
|
||||
|
||||
if( left->tm_hour > right->tm_hour )
|
||||
return 1;
|
||||
else if( left->tm_hour < right->tm_hour )
|
||||
return -1;
|
||||
|
||||
if( left->tm_min > right->tm_min )
|
||||
return 1;
|
||||
else if( left->tm_min < right->tm_min )
|
||||
return -1;
|
||||
|
||||
if( left->tm_sec > right->tm_sec )
|
||||
return 1;
|
||||
else if( left->tm_sec < right->tm_sec )
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Check if a date is safely inside a range.
|
||||
The intention is to check if its a few days inside.
|
||||
*/
|
||||
static int date_in_safe_range( const struct TM* date, const struct tm* min, const struct tm* max ) {
|
||||
if( cmp_date(date, min) == -1 )
|
||||
return 0;
|
||||
|
||||
if( cmp_date(date, max) == 1 )
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* timegm() is not in the C or POSIX spec, but it is such a useful
|
||||
extension I would be remiss in leaving it out. Also I need it
|
||||
for localtime64()
|
||||
*/
|
||||
Time64_T timegm64(const struct TM *date) {
|
||||
Time64_T days = 0;
|
||||
Time64_T seconds = 0;
|
||||
Year year;
|
||||
Year orig_year = (Year)date->tm_year;
|
||||
int cycles = 0;
|
||||
|
||||
if( orig_year > 100 ) {
|
||||
cycles = (orig_year - 100) / 400;
|
||||
orig_year -= cycles * 400;
|
||||
days += (Time64_T)cycles * days_in_gregorian_cycle;
|
||||
}
|
||||
else if( orig_year < -300 ) {
|
||||
cycles = (orig_year - 100) / 400;
|
||||
orig_year -= cycles * 400;
|
||||
days += (Time64_T)cycles * days_in_gregorian_cycle;
|
||||
}
|
||||
TIME64_TRACE3("# timegm/ cycles: %d, days: %lld, orig_year: %lld\n", cycles, days, orig_year);
|
||||
|
||||
if( orig_year > 70 ) {
|
||||
year = 70;
|
||||
while( year < orig_year ) {
|
||||
days += length_of_year[IS_LEAP(year)];
|
||||
year++;
|
||||
}
|
||||
}
|
||||
else if ( orig_year < 70 ) {
|
||||
year = 69;
|
||||
do {
|
||||
days -= length_of_year[IS_LEAP(year)];
|
||||
year--;
|
||||
} while( year >= orig_year );
|
||||
}
|
||||
|
||||
days += julian_days_by_month[IS_LEAP(orig_year)][date->tm_mon];
|
||||
days += date->tm_mday - 1;
|
||||
|
||||
seconds = days * 60 * 60 * 24;
|
||||
|
||||
seconds += date->tm_hour * 60 * 60;
|
||||
seconds += date->tm_min * 60;
|
||||
seconds += date->tm_sec;
|
||||
|
||||
return(seconds);
|
||||
}
|
||||
|
||||
|
||||
static int check_tm(struct TM *tm)
|
||||
{
|
||||
/* Don't forget leap seconds */
|
||||
assert(tm->tm_sec >= 0);
|
||||
assert(tm->tm_sec <= 61);
|
||||
|
||||
assert(tm->tm_min >= 0);
|
||||
assert(tm->tm_min <= 59);
|
||||
|
||||
assert(tm->tm_hour >= 0);
|
||||
assert(tm->tm_hour <= 23);
|
||||
|
||||
assert(tm->tm_mday >= 1);
|
||||
assert(tm->tm_mday <= days_in_month[IS_LEAP(tm->tm_year)][tm->tm_mon]);
|
||||
|
||||
assert(tm->tm_mon >= 0);
|
||||
assert(tm->tm_mon <= 11);
|
||||
|
||||
assert(tm->tm_wday >= 0);
|
||||
assert(tm->tm_wday <= 6);
|
||||
|
||||
assert(tm->tm_yday >= 0);
|
||||
assert(tm->tm_yday <= length_of_year[IS_LEAP(tm->tm_year)]);
|
||||
|
||||
#ifdef HAVE_TM_TM_GMTOFF
|
||||
assert(tm->tm_gmtoff >= -24 * 60 * 60);
|
||||
assert(tm->tm_gmtoff <= 24 * 60 * 60);
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* The exceptional centuries without leap years cause the cycle to
|
||||
shift by 16
|
||||
*/
|
||||
static Year cycle_offset(Year year)
|
||||
{
|
||||
const Year start_year = 2000;
|
||||
Year year_diff = year - start_year;
|
||||
Year exceptions;
|
||||
|
||||
if( year > start_year )
|
||||
year_diff--;
|
||||
|
||||
exceptions = year_diff / 100;
|
||||
exceptions -= year_diff / 400;
|
||||
|
||||
TIME64_TRACE3("# year: %lld, exceptions: %lld, year_diff: %lld\n",
|
||||
year, exceptions, year_diff);
|
||||
|
||||
return exceptions * 16;
|
||||
}
|
||||
|
||||
/* For a given year after 2038, pick the latest possible matching
|
||||
year in the 28 year calendar cycle.
|
||||
|
||||
A matching year...
|
||||
1) Starts on the same day of the week.
|
||||
2) Has the same leap year status.
|
||||
|
||||
This is so the calendars match up.
|
||||
|
||||
Also the previous year must match. When doing Jan 1st you might
|
||||
wind up on Dec 31st the previous year when doing a -UTC time zone.
|
||||
|
||||
Finally, the next year must have the same start day of week. This
|
||||
is for Dec 31st with a +UTC time zone.
|
||||
It doesn't need the same leap year status since we only care about
|
||||
January 1st.
|
||||
*/
|
||||
static int safe_year(const Year year)
|
||||
{
|
||||
int safe_year= (int)year;
|
||||
Year year_cycle;
|
||||
|
||||
if( year >= MIN_SAFE_YEAR && year <= MAX_SAFE_YEAR ) {
|
||||
return safe_year;
|
||||
}
|
||||
|
||||
year_cycle = year + cycle_offset(year);
|
||||
|
||||
/* safe_years_low is off from safe_years_high by 8 years */
|
||||
if( year < MIN_SAFE_YEAR )
|
||||
year_cycle -= 8;
|
||||
|
||||
/* Change non-leap xx00 years to an equivalent */
|
||||
if( is_exception_century(year) )
|
||||
year_cycle += 11;
|
||||
|
||||
/* Also xx01 years, since the previous year will be wrong */
|
||||
if( is_exception_century(year - 1) )
|
||||
year_cycle += 17;
|
||||
|
||||
year_cycle %= SOLAR_CYCLE_LENGTH;
|
||||
if( year_cycle < 0 )
|
||||
year_cycle = SOLAR_CYCLE_LENGTH + year_cycle;
|
||||
|
||||
assert( year_cycle >= 0 );
|
||||
assert( year_cycle < SOLAR_CYCLE_LENGTH );
|
||||
if( year < MIN_SAFE_YEAR )
|
||||
safe_year = safe_years_low[year_cycle];
|
||||
else if( year > MAX_SAFE_YEAR )
|
||||
safe_year = safe_years_high[year_cycle];
|
||||
else
|
||||
assert(0);
|
||||
|
||||
TIME64_TRACE3("# year: %lld, year_cycle: %lld, safe_year: %d\n",
|
||||
year, year_cycle, safe_year);
|
||||
|
||||
assert(safe_year <= MAX_SAFE_YEAR && safe_year >= MIN_SAFE_YEAR);
|
||||
|
||||
return safe_year;
|
||||
}
|
||||
|
||||
|
||||
void copy_tm_to_TM64(const struct tm *src, struct TM *dest) {
|
||||
if( src == NULL ) {
|
||||
memset(dest, 0, sizeof(*dest));
|
||||
}
|
||||
else {
|
||||
# ifdef USE_TM64
|
||||
dest->tm_sec = src->tm_sec;
|
||||
dest->tm_min = src->tm_min;
|
||||
dest->tm_hour = src->tm_hour;
|
||||
dest->tm_mday = src->tm_mday;
|
||||
dest->tm_mon = src->tm_mon;
|
||||
dest->tm_year = (Year)src->tm_year;
|
||||
dest->tm_wday = src->tm_wday;
|
||||
dest->tm_yday = src->tm_yday;
|
||||
dest->tm_isdst = src->tm_isdst;
|
||||
|
||||
# ifdef HAVE_TM_TM_GMTOFF
|
||||
dest->tm_gmtoff = src->tm_gmtoff;
|
||||
# endif
|
||||
|
||||
# ifdef HAVE_TM_TM_ZONE
|
||||
dest->tm_zone = src->tm_zone;
|
||||
# endif
|
||||
|
||||
# else
|
||||
/* They're the same type */
|
||||
memcpy(dest, src, sizeof(*dest));
|
||||
# endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void copy_TM64_to_tm(const struct TM *src, struct tm *dest) {
|
||||
if( src == NULL ) {
|
||||
memset(dest, 0, sizeof(*dest));
|
||||
}
|
||||
else {
|
||||
# ifdef USE_TM64
|
||||
dest->tm_sec = src->tm_sec;
|
||||
dest->tm_min = src->tm_min;
|
||||
dest->tm_hour = src->tm_hour;
|
||||
dest->tm_mday = src->tm_mday;
|
||||
dest->tm_mon = src->tm_mon;
|
||||
dest->tm_year = (int)src->tm_year;
|
||||
dest->tm_wday = src->tm_wday;
|
||||
dest->tm_yday = src->tm_yday;
|
||||
dest->tm_isdst = src->tm_isdst;
|
||||
|
||||
# ifdef HAVE_TM_TM_GMTOFF
|
||||
dest->tm_gmtoff = src->tm_gmtoff;
|
||||
# endif
|
||||
|
||||
# ifdef HAVE_TM_TM_ZONE
|
||||
dest->tm_zone = src->tm_zone;
|
||||
# endif
|
||||
|
||||
# else
|
||||
/* They're the same type */
|
||||
memcpy(dest, src, sizeof(*dest));
|
||||
# endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifndef HAVE_LOCALTIME_R
|
||||
/* Simulate localtime_r() to the best of our ability */
|
||||
static struct tm * fake_localtime_r(const time_t *time, struct tm *result) {
|
||||
const struct tm *static_result = localtime(time);
|
||||
|
||||
assert(result != NULL);
|
||||
|
||||
if( static_result == NULL ) {
|
||||
memset(result, 0, sizeof(*result));
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
memcpy(result, static_result, sizeof(*result));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef HAVE_GMTIME_R
|
||||
/* Simulate gmtime_r() to the best of our ability */
|
||||
static struct tm * fake_gmtime_r(const time_t *time, struct tm *result) {
|
||||
const struct tm *static_result = gmtime(time);
|
||||
|
||||
assert(result != NULL);
|
||||
|
||||
if( static_result == NULL ) {
|
||||
memset(result, 0, sizeof(*result));
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
memcpy(result, static_result, sizeof(*result));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static Time64_T seconds_between_years(Year left_year, Year right_year) {
|
||||
int increment = (left_year > right_year) ? 1 : -1;
|
||||
Time64_T seconds = 0;
|
||||
int cycles;
|
||||
|
||||
if( left_year > 2400 ) {
|
||||
cycles = (left_year - 2400) / 400;
|
||||
left_year -= cycles * 400;
|
||||
seconds += cycles * seconds_in_gregorian_cycle;
|
||||
}
|
||||
else if( left_year < 1600 ) {
|
||||
cycles = (left_year - 1600) / 400;
|
||||
left_year += cycles * 400;
|
||||
seconds += cycles * seconds_in_gregorian_cycle;
|
||||
}
|
||||
|
||||
while( left_year != right_year ) {
|
||||
seconds += length_of_year[IS_LEAP(right_year - 1900)] * 60 * 60 * 24;
|
||||
right_year += increment;
|
||||
}
|
||||
|
||||
return seconds * increment;
|
||||
}
|
||||
|
||||
|
||||
Time64_T mktime64(struct TM *input_date) {
|
||||
struct tm safe_date;
|
||||
struct TM date;
|
||||
Time64_T time;
|
||||
Year year = input_date->tm_year + 1900;
|
||||
|
||||
if( date_in_safe_range(input_date, &SYSTEM_MKTIME_MIN, &SYSTEM_MKTIME_MAX) )
|
||||
{
|
||||
copy_TM64_to_tm(input_date, &safe_date);
|
||||
time = (Time64_T)mktime(&safe_date);
|
||||
|
||||
/* Correct the possibly out of bound input date */
|
||||
copy_tm_to_TM64(&safe_date, input_date);
|
||||
return time;
|
||||
}
|
||||
|
||||
/* Have to make the year safe in date else it won't fit in safe_date */
|
||||
date = *input_date;
|
||||
date.tm_year = safe_year(year) - 1900;
|
||||
copy_TM64_to_tm(&date, &safe_date);
|
||||
|
||||
time = (Time64_T)mktime(&safe_date);
|
||||
|
||||
/* Correct the user's possibly out of bound input date */
|
||||
copy_tm_to_TM64(&safe_date, input_date);
|
||||
|
||||
time += seconds_between_years(year, (Year)(safe_date.tm_year + 1900));
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
|
||||
/* Because I think mktime() is a crappy name */
|
||||
Time64_T timelocal64(struct TM *date) {
|
||||
return mktime64(date);
|
||||
}
|
||||
|
||||
|
||||
struct TM *gmtime64_r (const Time64_T *in_time, struct TM *p)
|
||||
{
|
||||
int v_tm_sec, v_tm_min, v_tm_hour, v_tm_mon, v_tm_wday;
|
||||
Time64_T v_tm_tday;
|
||||
int leap;
|
||||
Time64_T m;
|
||||
Time64_T time = *in_time;
|
||||
Year year = 70;
|
||||
int cycles = 0;
|
||||
|
||||
assert(p != NULL);
|
||||
|
||||
/* Use the system gmtime() if time_t is small enough */
|
||||
if( SHOULD_USE_SYSTEM_GMTIME(*in_time) ) {
|
||||
time_t safe_time = (time_t)*in_time;
|
||||
struct tm safe_date;
|
||||
GMTIME_R(&safe_time, &safe_date);
|
||||
|
||||
copy_tm_to_TM64(&safe_date, p);
|
||||
assert(check_tm(p));
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
#ifdef HAVE_TM_TM_GMTOFF
|
||||
p->tm_gmtoff = 0;
|
||||
#endif
|
||||
p->tm_isdst = 0;
|
||||
|
||||
#ifdef HAVE_TM_TM_ZONE
|
||||
p->tm_zone = (char*)"UTC";
|
||||
#endif
|
||||
|
||||
v_tm_sec = (int)(time % 60);
|
||||
time /= 60;
|
||||
v_tm_min = (int)(time % 60);
|
||||
time /= 60;
|
||||
v_tm_hour = (int)(time % 24);
|
||||
time /= 24;
|
||||
v_tm_tday = time;
|
||||
|
||||
WRAP (v_tm_sec, v_tm_min, 60);
|
||||
WRAP (v_tm_min, v_tm_hour, 60);
|
||||
WRAP (v_tm_hour, v_tm_tday, 24);
|
||||
|
||||
v_tm_wday = (int)((v_tm_tday + 4) % 7);
|
||||
if (v_tm_wday < 0)
|
||||
v_tm_wday += 7;
|
||||
m = v_tm_tday;
|
||||
|
||||
if (m >= CHEAT_DAYS) {
|
||||
year = CHEAT_YEARS;
|
||||
m -= CHEAT_DAYS;
|
||||
}
|
||||
|
||||
if (m >= 0) {
|
||||
/* Gregorian cycles, this is huge optimization for distant times */
|
||||
cycles = (int)(m / (Time64_T) days_in_gregorian_cycle);
|
||||
if( cycles ) {
|
||||
m -= (cycles * (Time64_T) days_in_gregorian_cycle);
|
||||
year += (cycles * years_in_gregorian_cycle);
|
||||
}
|
||||
|
||||
/* Years */
|
||||
leap = IS_LEAP (year);
|
||||
while (m >= (Time64_T) length_of_year[leap]) {
|
||||
m -= (Time64_T) length_of_year[leap];
|
||||
year++;
|
||||
leap = IS_LEAP (year);
|
||||
}
|
||||
|
||||
/* Months */
|
||||
v_tm_mon = 0;
|
||||
while (m >= (Time64_T) days_in_month[leap][v_tm_mon]) {
|
||||
m -= (Time64_T) days_in_month[leap][v_tm_mon];
|
||||
v_tm_mon++;
|
||||
}
|
||||
} else {
|
||||
year--;
|
||||
|
||||
/* Gregorian cycles */
|
||||
cycles = (int)((m / (Time64_T) days_in_gregorian_cycle) + 1);
|
||||
if( cycles ) {
|
||||
m -= (cycles * (Time64_T) days_in_gregorian_cycle);
|
||||
year += (cycles * years_in_gregorian_cycle);
|
||||
}
|
||||
|
||||
/* Years */
|
||||
leap = IS_LEAP (year);
|
||||
while (m < (Time64_T) -length_of_year[leap]) {
|
||||
m += (Time64_T) length_of_year[leap];
|
||||
year--;
|
||||
leap = IS_LEAP (year);
|
||||
}
|
||||
|
||||
/* Months */
|
||||
v_tm_mon = 11;
|
||||
while (m < (Time64_T) -days_in_month[leap][v_tm_mon]) {
|
||||
m += (Time64_T) days_in_month[leap][v_tm_mon];
|
||||
v_tm_mon--;
|
||||
}
|
||||
m += (Time64_T) days_in_month[leap][v_tm_mon];
|
||||
}
|
||||
|
||||
p->tm_year = year;
|
||||
if( p->tm_year != year ) {
|
||||
#ifdef EOVERFLOW
|
||||
errno = EOVERFLOW;
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* At this point m is less than a year so casting to an int is safe */
|
||||
p->tm_mday = (int) m + 1;
|
||||
p->tm_yday = julian_days_by_month[leap][v_tm_mon] + (int)m;
|
||||
p->tm_sec = v_tm_sec;
|
||||
p->tm_min = v_tm_min;
|
||||
p->tm_hour = v_tm_hour;
|
||||
p->tm_mon = v_tm_mon;
|
||||
p->tm_wday = v_tm_wday;
|
||||
|
||||
assert(check_tm(p));
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
struct TM *localtime64_r (const Time64_T *time, struct TM *local_tm)
|
||||
{
|
||||
time_t safe_time;
|
||||
struct tm safe_date;
|
||||
struct TM gm_tm;
|
||||
Year orig_year;
|
||||
int month_diff;
|
||||
|
||||
assert(local_tm != NULL);
|
||||
|
||||
/* Use the system localtime() if time_t is small enough */
|
||||
if( SHOULD_USE_SYSTEM_LOCALTIME(*time) ) {
|
||||
safe_time = (time_t)*time;
|
||||
|
||||
TIME64_TRACE1("Using system localtime for %lld\n", *time);
|
||||
|
||||
LOCALTIME_R(&safe_time, &safe_date);
|
||||
|
||||
copy_tm_to_TM64(&safe_date, local_tm);
|
||||
assert(check_tm(local_tm));
|
||||
|
||||
return local_tm;
|
||||
}
|
||||
|
||||
if( gmtime64_r(time, &gm_tm) == NULL ) {
|
||||
TIME64_TRACE1("gmtime64_r returned null for %lld\n", *time);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
orig_year = gm_tm.tm_year;
|
||||
|
||||
if (gm_tm.tm_year > (2037 - 1900) ||
|
||||
gm_tm.tm_year < (1970 - 1900)
|
||||
)
|
||||
{
|
||||
TIME64_TRACE1("Mapping tm_year %lld to safe_year\n", (Year)gm_tm.tm_year);
|
||||
gm_tm.tm_year = safe_year((Year)(gm_tm.tm_year + 1900)) - 1900;
|
||||
}
|
||||
|
||||
safe_time = (time_t)timegm64(&gm_tm);
|
||||
if( LOCALTIME_R(&safe_time, &safe_date) == NULL ) {
|
||||
TIME64_TRACE1("localtime_r(%d) returned NULL\n", (int)safe_time);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
copy_tm_to_TM64(&safe_date, local_tm);
|
||||
|
||||
local_tm->tm_year = orig_year;
|
||||
if( local_tm->tm_year != orig_year ) {
|
||||
TIME64_TRACE2("tm_year overflow: tm_year %lld, orig_year %lld\n",
|
||||
(Year)local_tm->tm_year, (Year)orig_year);
|
||||
|
||||
#ifdef EOVERFLOW
|
||||
errno = EOVERFLOW;
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
month_diff = local_tm->tm_mon - gm_tm.tm_mon;
|
||||
|
||||
/* When localtime is Dec 31st previous year and
|
||||
gmtime is Jan 1st next year.
|
||||
*/
|
||||
if( month_diff == 11 ) {
|
||||
local_tm->tm_year--;
|
||||
}
|
||||
|
||||
/* When localtime is Jan 1st, next year and
|
||||
gmtime is Dec 31st, previous year.
|
||||
*/
|
||||
if( month_diff == -11 ) {
|
||||
local_tm->tm_year++;
|
||||
}
|
||||
|
||||
/* GMT is Jan 1st, xx01 year, but localtime is still Dec 31st
|
||||
in a non-leap xx00. There is one point in the cycle
|
||||
we can't account for which the safe xx00 year is a leap
|
||||
year. So we need to correct for Dec 31st comming out as
|
||||
the 366th day of the year.
|
||||
*/
|
||||
if( !IS_LEAP(local_tm->tm_year) && local_tm->tm_yday == 365 )
|
||||
local_tm->tm_yday--;
|
||||
|
||||
assert(check_tm(local_tm));
|
||||
|
||||
return local_tm;
|
||||
}
|
||||
|
||||
|
||||
static int valid_tm_wday( const struct TM* date ) {
|
||||
if( 0 <= date->tm_wday && date->tm_wday <= 6 )
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int valid_tm_mon( const struct TM* date ) {
|
||||
if( 0 <= date->tm_mon && date->tm_mon <= 11 )
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
char *asctime64_r( const struct TM* date, char *result ) {
|
||||
/* I figure everything else can be displayed, even hour 25, but if
|
||||
these are out of range we walk off the name arrays */
|
||||
if( !valid_tm_wday(date) || !valid_tm_mon(date) )
|
||||
return NULL;
|
||||
|
||||
sprintf(result, TM64_ASCTIME_FORMAT,
|
||||
wday_name[date->tm_wday],
|
||||
mon_name[date->tm_mon],
|
||||
date->tm_mday, date->tm_hour,
|
||||
date->tm_min, date->tm_sec,
|
||||
1900 + date->tm_year);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
char *ctime64_r( const Time64_T* time, char* result ) {
|
||||
struct TM date;
|
||||
|
||||
localtime64_r( time, &date );
|
||||
return asctime64_r( &date, result );
|
||||
}
|
||||
|
||||
81
lib/plist/time64.h
Executable file
81
lib/plist/time64.h
Executable file
@@ -0,0 +1,81 @@
|
||||
#ifndef TIME64_H
|
||||
# define TIME64_H
|
||||
|
||||
#include <time.h>
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
/* Set our custom types */
|
||||
typedef long long Int64;
|
||||
typedef Int64 Time64_T;
|
||||
typedef Int64 Year;
|
||||
|
||||
|
||||
/* A copy of the tm struct but with a 64 bit year */
|
||||
struct TM64 {
|
||||
int tm_sec;
|
||||
int tm_min;
|
||||
int tm_hour;
|
||||
int tm_mday;
|
||||
int tm_mon;
|
||||
Year tm_year;
|
||||
int tm_wday;
|
||||
int tm_yday;
|
||||
int tm_isdst;
|
||||
|
||||
#ifdef HAVE_TM_TM_GMTOFF
|
||||
long tm_gmtoff;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TM_TM_ZONE
|
||||
char *tm_zone;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/* Decide which tm struct to use */
|
||||
#ifdef USE_TM64
|
||||
#define TM TM64
|
||||
#else
|
||||
#define TM tm
|
||||
#endif
|
||||
|
||||
|
||||
/* Declare public functions */
|
||||
struct TM *gmtime64_r (const Time64_T *, struct TM *);
|
||||
struct TM *localtime64_r (const Time64_T *, struct TM *);
|
||||
|
||||
char *asctime64_r (const struct TM *, char *);
|
||||
|
||||
char *ctime64_r (const Time64_T*, char*);
|
||||
|
||||
Time64_T timegm64 (const struct TM *);
|
||||
Time64_T mktime64 (struct TM *);
|
||||
Time64_T timelocal64 (struct TM *);
|
||||
|
||||
|
||||
/* Not everyone has gm/localtime_r(), provide a replacement */
|
||||
#ifdef HAVE_LOCALTIME_R
|
||||
# define LOCALTIME_R(clock, result) localtime_r(clock, result)
|
||||
#else
|
||||
# define LOCALTIME_R(clock, result) fake_localtime_r(clock, result)
|
||||
#endif
|
||||
#ifdef HAVE_GMTIME_R
|
||||
# define GMTIME_R(clock, result) gmtime_r(clock, result)
|
||||
#else
|
||||
# define GMTIME_R(clock, result) fake_gmtime_r(clock, result)
|
||||
#endif
|
||||
|
||||
|
||||
/* Use a different asctime format depending on how big the year is */
|
||||
#ifdef USE_TM64
|
||||
#define TM64_ASCTIME_FORMAT "%.3s %.3s%3d %.2d:%.2d:%.2d %lld\n"
|
||||
#else
|
||||
#define TM64_ASCTIME_FORMAT "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n"
|
||||
#endif
|
||||
|
||||
void copy_tm_to_TM64(const struct tm *src, struct TM *dest);
|
||||
void copy_TM64_to_tm(const struct TM *src, struct tm *dest);
|
||||
|
||||
#endif
|
||||
97
lib/plist/time64_limits.h
Executable file
97
lib/plist/time64_limits.h
Executable file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
Maximum and minimum inputs your system's respective time functions
|
||||
can correctly handle. time64.h will use your system functions if
|
||||
the input falls inside these ranges and corresponding USE_SYSTEM_*
|
||||
constant is defined.
|
||||
*/
|
||||
|
||||
#ifndef TIME64_LIMITS_H
|
||||
#define TIME64_LIMITS_H
|
||||
|
||||
#include <time.h>
|
||||
|
||||
/* Max/min for localtime() */
|
||||
#define SYSTEM_LOCALTIME_MAX 2147483647
|
||||
#define SYSTEM_LOCALTIME_MIN -2147483647-1
|
||||
|
||||
/* Max/min for gmtime() */
|
||||
#define SYSTEM_GMTIME_MAX 2147483647
|
||||
#define SYSTEM_GMTIME_MIN -2147483647-1
|
||||
|
||||
/* Max/min for mktime() */
|
||||
static const struct tm SYSTEM_MKTIME_MAX = {
|
||||
7,
|
||||
14,
|
||||
19,
|
||||
18,
|
||||
0,
|
||||
138,
|
||||
1,
|
||||
17,
|
||||
0
|
||||
#ifdef HAVE_TM_TM_GMTOFF
|
||||
,-28800
|
||||
#endif
|
||||
#ifdef HAVE_TM_TM_ZONE
|
||||
,(char*)"PST"
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct tm SYSTEM_MKTIME_MIN = {
|
||||
52,
|
||||
45,
|
||||
12,
|
||||
13,
|
||||
11,
|
||||
1,
|
||||
5,
|
||||
346,
|
||||
0
|
||||
#ifdef HAVE_TM_TM_GMTOFF
|
||||
,-28800
|
||||
#endif
|
||||
#ifdef HAVE_TM_TM_ZONE
|
||||
,(char*)"PST"
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Max/min for timegm() */
|
||||
#ifdef HAVE_TIMEGM
|
||||
static const struct tm SYSTEM_TIMEGM_MAX = {
|
||||
7,
|
||||
14,
|
||||
3,
|
||||
19,
|
||||
0,
|
||||
138,
|
||||
2,
|
||||
18,
|
||||
0
|
||||
#ifdef HAVE_TM_TM_GMTOFF
|
||||
,0
|
||||
#endif
|
||||
#ifdef HAVE_TM_TM_ZONE
|
||||
,(char*)"UTC"
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct tm SYSTEM_TIMEGM_MIN = {
|
||||
52,
|
||||
45,
|
||||
20,
|
||||
13,
|
||||
11,
|
||||
1,
|
||||
5,
|
||||
346,
|
||||
0
|
||||
#ifdef HAVE_TM_TM_GMTOFF
|
||||
,0
|
||||
#endif
|
||||
#ifdef HAVE_TM_TM_ZONE
|
||||
,(char*)"UTC"
|
||||
#endif
|
||||
};
|
||||
#endif /* HAVE_TIMEGM */
|
||||
|
||||
#endif /* TIME64_LIMITS_H */
|
||||
1438
lib/plist/xplist.c
Executable file
1438
lib/plist/xplist.c
Executable file
File diff suppressed because it is too large
Load Diff
380
lib/raop.c
Executable file
380
lib/raop.c
Executable file
@@ -0,0 +1,380 @@
|
||||
/**
|
||||
* 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "raop.h"
|
||||
#include "raop_rtp.h"
|
||||
#include "raop_rtp.h"
|
||||
#include "pairing.h"
|
||||
#include "httpd.h"
|
||||
|
||||
#include "global.h"
|
||||
#include "fairplay.h"
|
||||
#include "netutils.h"
|
||||
#include "logger.h"
|
||||
#include "compat.h"
|
||||
#include "raop_rtp_mirror.h"
|
||||
#include "raop_ntp.h"
|
||||
|
||||
struct raop_s {
|
||||
/* Callbacks for audio and video */
|
||||
raop_callbacks_t callbacks;
|
||||
|
||||
/* Logger instance */
|
||||
logger_t *logger;
|
||||
|
||||
/* Pairing, HTTP daemon and RSA key */
|
||||
pairing_t *pairing;
|
||||
httpd_t *httpd;
|
||||
|
||||
dnssd_t *dnssd;
|
||||
|
||||
unsigned short port;
|
||||
};
|
||||
|
||||
struct raop_conn_s {
|
||||
raop_t *raop;
|
||||
raop_ntp_t *raop_ntp;
|
||||
raop_rtp_t *raop_rtp;
|
||||
raop_rtp_mirror_t *raop_rtp_mirror;
|
||||
fairplay_t *fairplay;
|
||||
pairing_session_t *pairing;
|
||||
|
||||
unsigned char *local;
|
||||
int locallen;
|
||||
|
||||
unsigned char *remote;
|
||||
int remotelen;
|
||||
|
||||
};
|
||||
typedef struct raop_conn_s raop_conn_t;
|
||||
|
||||
#include "raop_handlers.h"
|
||||
|
||||
static void *
|
||||
conn_init(void *opaque, unsigned char *local, int locallen, unsigned char *remote, int remotelen) {
|
||||
raop_t *raop = opaque;
|
||||
raop_conn_t *conn;
|
||||
|
||||
assert(raop);
|
||||
|
||||
conn = calloc(1, sizeof(raop_conn_t));
|
||||
if (!conn) {
|
||||
return NULL;
|
||||
}
|
||||
conn->raop = raop;
|
||||
conn->raop_rtp = NULL;
|
||||
conn->raop_ntp = NULL;
|
||||
conn->fairplay = fairplay_init(raop->logger);
|
||||
|
||||
if (!conn->fairplay) {
|
||||
free(conn);
|
||||
return NULL;
|
||||
}
|
||||
conn->pairing = pairing_session_init(raop->pairing);
|
||||
if (!conn->pairing) {
|
||||
fairplay_destroy(conn->fairplay);
|
||||
free(conn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (locallen == 4) {
|
||||
logger_log(conn->raop->logger, LOGGER_INFO,
|
||||
"Local: %d.%d.%d.%d",
|
||||
local[0], local[1], local[2], local[3]);
|
||||
} else if (locallen == 16) {
|
||||
logger_log(conn->raop->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->raop->logger, LOGGER_INFO,
|
||||
"Remote: %d.%d.%d.%d",
|
||||
remote[0], remote[1], remote[2], remote[3]);
|
||||
} else if (remotelen == 16) {
|
||||
logger_log(conn->raop->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;
|
||||
|
||||
if (raop->callbacks.conn_init) {
|
||||
raop->callbacks.conn_init(raop->callbacks.cls);
|
||||
}
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
static void
|
||||
conn_request(void *ptr, http_request_t *request, http_response_t **response) {
|
||||
raop_conn_t *conn = ptr;
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "conn_request");
|
||||
const char *method;
|
||||
const char *url;
|
||||
const char *cseq;
|
||||
|
||||
char *response_data = NULL;
|
||||
int response_datalen = 0;
|
||||
|
||||
method = http_request_get_method(request);
|
||||
url = http_request_get_url(request);
|
||||
cseq = http_request_get_header(request, "CSeq");
|
||||
if (!method || !cseq) {
|
||||
return;
|
||||
}
|
||||
|
||||
*response = http_response_init("RTSP/1.0", 200, "OK");
|
||||
|
||||
http_response_add_header(*response, "CSeq", cseq);
|
||||
//http_response_add_header(*response, "Apple-Jack-Status", "connected; type=analog");
|
||||
http_response_add_header(*response, "Server", "AirTunes/220.68");
|
||||
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "Handling request %s with URL %s", method, url);
|
||||
raop_handler_t handler = NULL;
|
||||
if (!strcmp(method, "GET") && !strcmp(url, "/info")) {
|
||||
handler = &raop_handler_info;
|
||||
} else if (!strcmp(method, "POST") && !strcmp(url, "/pair-setup")) {
|
||||
handler = &raop_handler_pairsetup;
|
||||
} else if (!strcmp(method, "POST") && !strcmp(url, "/pair-verify")) {
|
||||
handler = &raop_handler_pairverify;
|
||||
} else if (!strcmp(method, "POST") && !strcmp(url, "/fp-setup")) {
|
||||
handler = &raop_handler_fpsetup;
|
||||
} else if (!strcmp(method, "OPTIONS")) {
|
||||
handler = &raop_handler_options;
|
||||
} else if (!strcmp(method, "SETUP")) {
|
||||
handler = &raop_handler_setup;
|
||||
} else if (!strcmp(method, "GET_PARAMETER")) {
|
||||
handler = &raop_handler_get_parameter;
|
||||
} else if (!strcmp(method, "SET_PARAMETER")) {
|
||||
handler = &raop_handler_set_parameter;
|
||||
} else if (!strcmp(method, "POST") && !strcmp(url, "/feedback")) {
|
||||
handler = &raop_handler_feedback;
|
||||
} else if (!strcmp(method, "RECORD")) {
|
||||
handler = &raop_handler_record;
|
||||
} else if (!strcmp(method, "FLUSH")) {
|
||||
const char *rtpinfo;
|
||||
int next_seq = -1;
|
||||
|
||||
rtpinfo = http_request_get_header(request, "RTP-Info");
|
||||
if (rtpinfo) {
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "Flush with RTP-Info: %s", rtpinfo);
|
||||
if (!strncmp(rtpinfo, "seq=", 4)) {
|
||||
next_seq = strtol(rtpinfo + 4, NULL, 10);
|
||||
}
|
||||
}
|
||||
if (conn->raop_rtp) {
|
||||
raop_rtp_flush(conn->raop_rtp, next_seq);
|
||||
} else {
|
||||
logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at FLUSH");
|
||||
}
|
||||
} else if (!strcmp(method, "TEARDOWN")) {
|
||||
//http_response_add_header(*response, "Connection", "close");
|
||||
if (conn->raop_rtp != NULL && raop_rtp_is_running(conn->raop_rtp)) {
|
||||
/* Destroy our RTP session */
|
||||
raop_rtp_stop(conn->raop_rtp);
|
||||
} else if (conn->raop_rtp_mirror) {
|
||||
/* Destroy our sessions */
|
||||
raop_rtp_destroy(conn->raop_rtp);
|
||||
conn->raop_rtp = NULL;
|
||||
raop_rtp_mirror_destroy(conn->raop_rtp_mirror);
|
||||
conn->raop_rtp_mirror = NULL;
|
||||
}
|
||||
}
|
||||
if (handler != NULL) {
|
||||
handler(conn, request, *response, &response_data, &response_datalen);
|
||||
}
|
||||
http_response_finish(*response, response_data, response_datalen);
|
||||
if (response_data) {
|
||||
free(response_data);
|
||||
response_data = NULL;
|
||||
response_datalen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
conn_destroy(void *ptr) {
|
||||
raop_conn_t *conn = ptr;
|
||||
|
||||
logger_log(conn->raop->logger, LOGGER_INFO, "Destroying connection");
|
||||
|
||||
if (conn->raop->callbacks.conn_destroy) {
|
||||
conn->raop->callbacks.conn_destroy(conn->raop->callbacks.cls);
|
||||
}
|
||||
|
||||
if (conn->raop_ntp) {
|
||||
raop_ntp_destroy(conn->raop_ntp);
|
||||
}
|
||||
if (conn->raop_rtp) {
|
||||
/* This is done in case TEARDOWN was not called */
|
||||
raop_rtp_destroy(conn->raop_rtp);
|
||||
}
|
||||
if (conn->raop_rtp_mirror) {
|
||||
/* This is done in case TEARDOWN was not called */
|
||||
raop_rtp_mirror_destroy(conn->raop_rtp_mirror);
|
||||
}
|
||||
|
||||
conn->raop->callbacks.video_flush(conn->raop->callbacks.cls);
|
||||
|
||||
free(conn->local);
|
||||
free(conn->remote);
|
||||
pairing_session_destroy(conn->pairing);
|
||||
fairplay_destroy(conn->fairplay);
|
||||
free(conn);
|
||||
}
|
||||
|
||||
raop_t *
|
||||
raop_init(int max_clients, raop_callbacks_t *callbacks) {
|
||||
raop_t *raop;
|
||||
pairing_t *pairing;
|
||||
httpd_t *httpd;
|
||||
httpd_callbacks_t httpd_cbs;
|
||||
|
||||
assert(callbacks);
|
||||
assert(max_clients > 0);
|
||||
assert(max_clients < 100);
|
||||
|
||||
/* Initialize the network */
|
||||
if (netutils_init() < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Validate the callbacks structure */
|
||||
if (!callbacks->audio_process ||
|
||||
!callbacks->video_process) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Allocate the raop_t structure */
|
||||
raop = calloc(1, sizeof(raop_t));
|
||||
if (!raop) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialize the logger */
|
||||
raop->logger = logger_init();
|
||||
pairing = pairing_init_generate();
|
||||
if (!pairing) {
|
||||
free(raop);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Set HTTP callbacks to our handlers */
|
||||
memset(&httpd_cbs, 0, sizeof(httpd_cbs));
|
||||
httpd_cbs.opaque = raop;
|
||||
httpd_cbs.conn_init = &conn_init;
|
||||
httpd_cbs.conn_request = &conn_request;
|
||||
httpd_cbs.conn_destroy = &conn_destroy;
|
||||
|
||||
/* Initialize the http daemon */
|
||||
httpd = httpd_init(raop->logger, &httpd_cbs, max_clients);
|
||||
if (!httpd) {
|
||||
pairing_destroy(pairing);
|
||||
free(raop);
|
||||
return NULL;
|
||||
}
|
||||
/* Copy callbacks structure */
|
||||
memcpy(&raop->callbacks, callbacks, sizeof(raop_callbacks_t));
|
||||
raop->pairing = pairing;
|
||||
raop->httpd = httpd;
|
||||
return raop;
|
||||
}
|
||||
|
||||
void
|
||||
raop_destroy(raop_t *raop) {
|
||||
if (raop) {
|
||||
raop_stop(raop);
|
||||
pairing_destroy(raop->pairing);
|
||||
httpd_destroy(raop->httpd);
|
||||
logger_destroy(raop->logger);
|
||||
free(raop);
|
||||
|
||||
/* Cleanup the network */
|
||||
netutils_cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
raop_is_running(raop_t *raop) {
|
||||
assert(raop);
|
||||
|
||||
return httpd_is_running(raop->httpd);
|
||||
}
|
||||
|
||||
void
|
||||
raop_set_log_level(raop_t *raop, int level) {
|
||||
assert(raop);
|
||||
|
||||
logger_set_level(raop->logger, level);
|
||||
}
|
||||
|
||||
void
|
||||
raop_set_port(raop_t *raop, unsigned short port) {
|
||||
assert(raop);
|
||||
raop->port = port;
|
||||
}
|
||||
|
||||
unsigned short
|
||||
raop_get_port(raop_t *raop) {
|
||||
assert(raop);
|
||||
return raop->port;
|
||||
}
|
||||
|
||||
void *
|
||||
raop_get_callback_cls(raop_t *raop) {
|
||||
assert(raop);
|
||||
return raop->callbacks.cls;
|
||||
}
|
||||
|
||||
void
|
||||
raop_set_log_callback(raop_t *raop, raop_log_callback_t callback, void *cls) {
|
||||
assert(raop);
|
||||
|
||||
logger_set_callback(raop->logger, callback, cls);
|
||||
}
|
||||
|
||||
void
|
||||
raop_set_dnssd(raop_t *raop, dnssd_t *dnssd) {
|
||||
assert(dnssd);
|
||||
raop->dnssd = dnssd;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
raop_start(raop_t *raop, unsigned short *port) {
|
||||
assert(raop);
|
||||
assert(port);
|
||||
return httpd_start(raop->httpd, port);
|
||||
}
|
||||
|
||||
void
|
||||
raop_stop(raop_t *raop) {
|
||||
assert(raop);
|
||||
httpd_stop(raop->httpd);
|
||||
}
|
||||
69
lib/raop.h
Executable file
69
lib/raop.h
Executable file
@@ -0,0 +1,69 @@
|
||||
#ifndef RAOP_H
|
||||
#define RAOP_H
|
||||
|
||||
#include "dnssd.h"
|
||||
#include "stream.h"
|
||||
#include "raop_ntp.h"
|
||||
|
||||
#if defined (WIN32) && defined(DLL_EXPORT)
|
||||
# define RAOP_API __declspec(dllexport)
|
||||
#else
|
||||
# define RAOP_API
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* Define syslog style log levels */
|
||||
#define RAOP_LOG_EMERG 0 /* system is unusable */
|
||||
#define RAOP_LOG_ALERT 1 /* action must be taken immediately */
|
||||
#define RAOP_LOG_CRIT 2 /* critical conditions */
|
||||
#define RAOP_LOG_ERR 3 /* error conditions */
|
||||
#define RAOP_LOG_WARNING 4 /* warning conditions */
|
||||
#define RAOP_LOG_NOTICE 5 /* normal but significant condition */
|
||||
#define RAOP_LOG_INFO 6 /* informational */
|
||||
#define RAOP_LOG_DEBUG 7 /* debug-level messages */
|
||||
|
||||
|
||||
typedef struct raop_s raop_t;
|
||||
|
||||
typedef void (*raop_log_callback_t)(void *cls, int level, const char *msg);
|
||||
|
||||
struct raop_callbacks_s {
|
||||
void* cls;
|
||||
|
||||
void (*audio_process)(void *cls, raop_ntp_t *ntp, aac_decode_struct *data);
|
||||
void (*video_process)(void *cls, raop_ntp_t *ntp, h264_decode_struct *data);
|
||||
|
||||
/* Optional but recommended callback functions */
|
||||
void (*conn_init)(void *cls);
|
||||
void (*conn_destroy)(void *cls);
|
||||
void (*audio_flush)(void *cls);
|
||||
void (*video_flush)(void *cls);
|
||||
void (*audio_set_volume)(void *cls, float volume);
|
||||
void (*audio_set_metadata)(void *cls, const void *buffer, int buflen);
|
||||
void (*audio_set_coverart)(void *cls, const void *buffer, int buflen);
|
||||
void (*audio_remote_control_id)(void *cls, const char *dacp_id, const char *active_remote_header);
|
||||
void (*audio_set_progress)(void *cls, unsigned int start, unsigned int curr, unsigned int end);
|
||||
};
|
||||
typedef struct raop_callbacks_s raop_callbacks_t;
|
||||
|
||||
RAOP_API raop_t *raop_init(int max_clients, raop_callbacks_t *callbacks);
|
||||
|
||||
RAOP_API void raop_set_log_level(raop_t *raop, int level);
|
||||
RAOP_API void raop_set_log_callback(raop_t *raop, raop_log_callback_t callback, void *cls);
|
||||
RAOP_API void raop_set_port(raop_t *raop, unsigned short port);
|
||||
RAOP_API unsigned short raop_get_port(raop_t *raop);
|
||||
RAOP_API void *raop_get_callback_cls(raop_t *raop);
|
||||
RAOP_API int raop_start(raop_t *raop, unsigned short *port);
|
||||
RAOP_API int raop_is_running(raop_t *raop);
|
||||
RAOP_API void raop_stop(raop_t *raop);
|
||||
RAOP_API void raop_set_dnssd(raop_t *raop, dnssd_t *dnssd);
|
||||
RAOP_API void raop_destroy(raop_t *raop);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
332
lib/raop_buffer.c
Executable file
332
lib/raop_buffer.c
Executable file
@@ -0,0 +1,332 @@
|
||||
/**
|
||||
* 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "raop_buffer.h"
|
||||
#include "raop_rtp.h"
|
||||
|
||||
#include "crypto.h"
|
||||
#include "compat.h"
|
||||
#include "stream.h"
|
||||
|
||||
#define RAOP_BUFFER_LENGTH 32
|
||||
|
||||
typedef struct {
|
||||
/* Data available */
|
||||
int filled;
|
||||
|
||||
/* RTP header */
|
||||
unsigned short seqnum;
|
||||
uint64_t timestamp;
|
||||
|
||||
/* Payload data */
|
||||
unsigned int payload_size;
|
||||
void *payload_data;
|
||||
} raop_buffer_entry_t;
|
||||
|
||||
struct raop_buffer_s {
|
||||
logger_t *logger;
|
||||
/* Key and IV used for decryption */
|
||||
unsigned char aeskey[RAOP_AESKEY_LEN];
|
||||
unsigned char aesiv[RAOP_AESIV_LEN];
|
||||
|
||||
/* First and last seqnum */
|
||||
int is_empty;
|
||||
unsigned short first_seqnum;
|
||||
unsigned short last_seqnum;
|
||||
|
||||
/* RTP buffer entries */
|
||||
raop_buffer_entry_t entries[RAOP_BUFFER_LENGTH];
|
||||
};
|
||||
|
||||
void
|
||||
raop_buffer_init_key_iv(raop_buffer_t *raop_buffer,
|
||||
const unsigned char *aeskey,
|
||||
const unsigned char *aesiv,
|
||||
const unsigned char *ecdh_secret)
|
||||
{
|
||||
|
||||
// Initialization key
|
||||
unsigned char eaeskey[64];
|
||||
memcpy(eaeskey, aeskey, 16);
|
||||
|
||||
sha_ctx_t *ctx = sha_init();
|
||||
sha_update(ctx, eaeskey, 16);
|
||||
sha_update(ctx, ecdh_secret, 32);
|
||||
sha_final(ctx, eaeskey, NULL);
|
||||
sha_destroy(ctx);
|
||||
|
||||
memcpy(raop_buffer->aeskey, eaeskey, 16);
|
||||
memcpy(raop_buffer->aesiv, aesiv, RAOP_AESIV_LEN);
|
||||
|
||||
#ifdef DUMP_AUDIO
|
||||
if (file_keyiv != NULL) {
|
||||
fwrite(raop_buffer->aeskey, 16, 1, file_keyiv);
|
||||
fwrite(raop_buffer->aesiv, 16, 1, file_keyiv);
|
||||
fclose(file_keyiv);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
raop_buffer_t *
|
||||
raop_buffer_init(logger_t *logger,
|
||||
const unsigned char *aeskey,
|
||||
const unsigned char *aesiv,
|
||||
const unsigned char *ecdh_secret)
|
||||
{
|
||||
raop_buffer_t *raop_buffer;
|
||||
assert(aeskey);
|
||||
assert(aesiv);
|
||||
assert(ecdh_secret);
|
||||
raop_buffer = calloc(1, sizeof(raop_buffer_t));
|
||||
if (!raop_buffer) {
|
||||
return NULL;
|
||||
}
|
||||
raop_buffer->logger = logger;
|
||||
raop_buffer_init_key_iv(raop_buffer, aeskey, aesiv, ecdh_secret);
|
||||
|
||||
for (int i = 0; i < RAOP_BUFFER_LENGTH; i++) {
|
||||
raop_buffer_entry_t *entry = &raop_buffer->entries[i];
|
||||
entry->payload_data = NULL;
|
||||
entry->payload_size = 0;
|
||||
}
|
||||
|
||||
raop_buffer->is_empty = 1;
|
||||
|
||||
return raop_buffer;
|
||||
}
|
||||
|
||||
void
|
||||
raop_buffer_destroy(raop_buffer_t *raop_buffer)
|
||||
{
|
||||
for (int i = 0; i < RAOP_BUFFER_LENGTH; i++) {
|
||||
raop_buffer_entry_t *entry = &raop_buffer->entries[i];
|
||||
if (entry->payload_data != NULL) {
|
||||
free(entry->payload_data);
|
||||
}
|
||||
}
|
||||
|
||||
if (raop_buffer) {
|
||||
free(raop_buffer);
|
||||
}
|
||||
|
||||
#ifdef DUMP_AUDIO
|
||||
if (file_aac != NULL) {
|
||||
fclose(file_aac);
|
||||
}
|
||||
if (file_source != NULL) {
|
||||
fclose(file_source);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
static short
|
||||
seqnum_cmp(unsigned short s1, unsigned short s2)
|
||||
{
|
||||
return (s1 - s2);
|
||||
}
|
||||
|
||||
//#define DUMP_AUDIO
|
||||
|
||||
#ifdef DUMP_AUDIO
|
||||
static FILE* file_aac = NULL;
|
||||
static FILE* file_source = NULL;
|
||||
static FILE* file_keyiv = NULL;
|
||||
#endif
|
||||
|
||||
|
||||
int
|
||||
raop_buffer_decrypt(raop_buffer_t *raop_buffer, unsigned char *data, unsigned char* output, unsigned int payload_size, unsigned int *outputlen)
|
||||
{
|
||||
assert(raop_buffer);
|
||||
int encryptedlen;
|
||||
#ifdef DUMP_AUDIO
|
||||
if (file_aac == NULL) {
|
||||
file_aac = fopen("/home/pi/Airplay.aac", "wb");
|
||||
file_source = fopen("/home/pi/Airplay.source", "wb");
|
||||
file_keyiv = fopen("/home/pi/Airplay.keyiv", "wb");
|
||||
}
|
||||
// Undecrypted file
|
||||
if (file_source != NULL) {
|
||||
fwrite(&data[12], payloadsize, 1, file_source);
|
||||
}
|
||||
#endif
|
||||
|
||||
encryptedlen = payload_size / 16*16;
|
||||
memset(output, 0, payload_size);
|
||||
// Need to be initialized internally
|
||||
aes_ctx_t *aes_ctx_audio = aes_cbc_init(raop_buffer->aeskey, raop_buffer->aesiv, AES_DECRYPT);
|
||||
aes_cbc_decrypt(aes_ctx_audio, &data[12], output, encryptedlen);
|
||||
aes_cbc_destroy(aes_ctx_audio);
|
||||
|
||||
memcpy(output + encryptedlen, &data[12 + encryptedlen], payload_size - encryptedlen);
|
||||
*outputlen = payload_size;
|
||||
|
||||
#ifdef DUMP_AUDIO
|
||||
// Decrypted file
|
||||
if (file_aac != NULL) {
|
||||
fwrite(output, payloadsize, 1, file_aac);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
raop_buffer_enqueue(raop_buffer_t *raop_buffer, unsigned char *data, unsigned short datalen, uint64_t timestamp, int use_seqnum) {
|
||||
assert(raop_buffer);
|
||||
|
||||
/* Check packet data length is valid */
|
||||
if (datalen < 12 || datalen > RAOP_PACKET_LEN) {
|
||||
return -1;
|
||||
}
|
||||
if (datalen == 16 && data[12] == 0x0 && data[13] == 0x68 && data[14] == 0x34 && data[15] == 0x0) {
|
||||
return 0;
|
||||
}
|
||||
int payload_size = datalen - 12;
|
||||
|
||||
/* Get correct seqnum for the packet */
|
||||
unsigned short seqnum;
|
||||
if (use_seqnum) {
|
||||
seqnum = (data[2] << 8) | data[3];
|
||||
} else {
|
||||
seqnum = raop_buffer->first_seqnum;
|
||||
}
|
||||
|
||||
/* If this packet is too late, just skip it */
|
||||
if (!raop_buffer->is_empty && seqnum_cmp(seqnum, raop_buffer->first_seqnum) < 0) {
|
||||
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) {
|
||||
raop_buffer_flush(raop_buffer, seqnum);
|
||||
}
|
||||
|
||||
/* Get entry corresponding our seqnum */
|
||||
raop_buffer_entry_t *entry = &raop_buffer->entries[seqnum % RAOP_BUFFER_LENGTH];
|
||||
if (entry->filled && seqnum_cmp(entry->seqnum, seqnum) == 0) {
|
||||
/* Packet resend, we can safely ignore */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Update the raop_buffer entry header */
|
||||
entry->seqnum = seqnum;
|
||||
entry->timestamp = timestamp;
|
||||
entry->filled = 1;
|
||||
|
||||
entry->payload_data = malloc(payload_size);
|
||||
int decrypt_ret = raop_buffer_decrypt(raop_buffer, data, entry->payload_data, payload_size, &entry->payload_size);
|
||||
assert(decrypt_ret >= 0);
|
||||
assert(entry->payload_size <= payload_size);
|
||||
|
||||
/* Update the raop_buffer seqnums */
|
||||
if (raop_buffer->is_empty) {
|
||||
raop_buffer->first_seqnum = seqnum;
|
||||
raop_buffer->last_seqnum = seqnum;
|
||||
raop_buffer->is_empty = 0;
|
||||
}
|
||||
if (seqnum_cmp(seqnum, raop_buffer->last_seqnum) > 0) {
|
||||
raop_buffer->last_seqnum = seqnum;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void *
|
||||
raop_buffer_dequeue(raop_buffer_t *raop_buffer, unsigned int *length, uint64_t *timestamp, int no_resend) {
|
||||
assert(raop_buffer);
|
||||
|
||||
/* Calculate number of entries in the current buffer */
|
||||
short entry_count = seqnum_cmp(raop_buffer->last_seqnum, raop_buffer->first_seqnum)+1;
|
||||
|
||||
/* Cannot dequeue from empty buffer */
|
||||
if (raop_buffer->is_empty || entry_count <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get the first buffer entry for inspection */
|
||||
raop_buffer_entry_t *entry = &raop_buffer->entries[raop_buffer->first_seqnum % RAOP_BUFFER_LENGTH];
|
||||
if (no_resend) {
|
||||
/* If we do no resends, always return the first entry */
|
||||
} else if (!entry->filled) {
|
||||
/* Check how much we have space left in the buffer */
|
||||
if (entry_count < RAOP_BUFFER_LENGTH) {
|
||||
/* Return nothing and hope resend gets on time */
|
||||
return NULL;
|
||||
}
|
||||
/* Risk of buffer overrun, return empty buffer */
|
||||
}
|
||||
|
||||
/* Update buffer and validate entry */
|
||||
raop_buffer->first_seqnum += 1;
|
||||
if (!entry->filled) {
|
||||
return NULL;
|
||||
}
|
||||
entry->filled = 0;
|
||||
|
||||
/* Return entry payload buffer */
|
||||
*timestamp = entry->timestamp;
|
||||
*length = entry->payload_size;
|
||||
entry->payload_size = 0;
|
||||
void* data = entry->payload_data;
|
||||
entry->payload_data = NULL;
|
||||
return data;
|
||||
}
|
||||
|
||||
void raop_buffer_handle_resends(raop_buffer_t *raop_buffer, raop_resend_cb_t resend_cb, void *opaque) {
|
||||
assert(raop_buffer);
|
||||
assert(resend_cb);
|
||||
|
||||
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++) {
|
||||
raop_buffer_entry_t *entry = &raop_buffer->entries[seqnum % RAOP_BUFFER_LENGTH];
|
||||
if (entry->filled) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (seqnum_cmp(seqnum, raop_buffer->first_seqnum) == 0) {
|
||||
return;
|
||||
}
|
||||
count = seqnum_cmp(seqnum, raop_buffer->first_seqnum);
|
||||
resend_cb(opaque, raop_buffer->first_seqnum, count);
|
||||
}
|
||||
}
|
||||
|
||||
void raop_buffer_flush(raop_buffer_t *raop_buffer, int next_seq) {
|
||||
assert(raop_buffer);
|
||||
|
||||
for (int i = 0; i < RAOP_BUFFER_LENGTH; i++) {
|
||||
if (raop_buffer->entries[i].payload_data) {
|
||||
free(raop_buffer->entries[i].payload_data);
|
||||
raop_buffer->entries[i].payload_size = 0;
|
||||
}
|
||||
raop_buffer->entries[i].filled = 0;
|
||||
}
|
||||
if (next_seq < 0 || next_seq > 0xffff) {
|
||||
raop_buffer->is_empty = 1;
|
||||
} else {
|
||||
raop_buffer->first_seqnum = next_seq;
|
||||
raop_buffer->last_seqnum = next_seq - 1;
|
||||
}
|
||||
}
|
||||
38
lib/raop_buffer.h
Executable file
38
lib/raop_buffer.h
Executable file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef RAOP_BUFFER_H
|
||||
#define RAOP_BUFFER_H
|
||||
|
||||
#include "logger.h"
|
||||
#include "raop_rtp.h"
|
||||
|
||||
typedef struct raop_buffer_s raop_buffer_t;
|
||||
|
||||
typedef int (*raop_resend_cb_t)(void *opaque, unsigned short seqno, unsigned short count);
|
||||
|
||||
raop_buffer_t *raop_buffer_init(logger_t *logger,
|
||||
const unsigned char *aeskey,
|
||||
const unsigned char *aesiv,
|
||||
const unsigned char *ecdh_secret);
|
||||
int raop_buffer_enqueue(raop_buffer_t *raop_buffer, unsigned char *data, unsigned short datalen, uint64_t timestamp, int use_seqnum);
|
||||
void *raop_buffer_dequeue(raop_buffer_t *raop_buffer, unsigned int *length, uint64_t *timestamp, int no_resend);
|
||||
void raop_buffer_handle_resends(raop_buffer_t *raop_buffer, raop_resend_cb_t resend_cb, void *opaque);
|
||||
void raop_buffer_flush(raop_buffer_t *raop_buffer, int next_seq);
|
||||
|
||||
int raop_buffer_decrypt(raop_buffer_t *raop_buffer, unsigned char *data, unsigned char* output,
|
||||
unsigned int datalen, unsigned int *outputlen);
|
||||
void raop_buffer_destroy(raop_buffer_t *raop_buffer);
|
||||
|
||||
#endif
|
||||
574
lib/raop_handlers.h
Executable file
574
lib/raop_handlers.h
Executable file
@@ -0,0 +1,574 @@
|
||||
/**
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
/* This file should be only included from raop.c as it defines static handler
|
||||
* functions and depends on raop internals */
|
||||
|
||||
#include "plist/plist/plist.h"
|
||||
#include "dnssdint.h"
|
||||
#include "utils.h"
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef void (*raop_handler_t)(raop_conn_t *, http_request_t *,
|
||||
http_response_t *, char **, int *);
|
||||
|
||||
static void
|
||||
raop_handler_info(raop_conn_t *conn,
|
||||
http_request_t *request, http_response_t *response,
|
||||
char **response_data, int *response_datalen)
|
||||
{
|
||||
assert(conn->raop->dnssd);
|
||||
|
||||
int airplay_txt_len = 0;
|
||||
const char *airplay_txt = dnssd_get_airplay_txt(conn->raop->dnssd, &airplay_txt_len);
|
||||
|
||||
int name_len = 0;
|
||||
const char *name = dnssd_get_name(conn->raop->dnssd, &name_len);
|
||||
|
||||
int hw_addr_raw_len = 0;
|
||||
const char *hw_addr_raw = dnssd_get_hw_addr(conn->raop->dnssd, &hw_addr_raw_len);
|
||||
|
||||
char *hw_addr = calloc(1, 3 * hw_addr_raw_len);
|
||||
int hw_addr_len = utils_hwaddr_airplay(hw_addr, 3 * hw_addr_raw_len, hw_addr_raw, hw_addr_raw_len);
|
||||
|
||||
int pk_len = 0;
|
||||
char *pk = utils_parse_hex(AIRPLAY_PK, strlen(AIRPLAY_PK), &pk_len);
|
||||
|
||||
plist_t r_node = plist_new_dict();
|
||||
|
||||
plist_t txt_airplay_node = plist_new_data(airplay_txt, airplay_txt_len);
|
||||
plist_dict_set_item(r_node, "txtAirPlay", txt_airplay_node);
|
||||
|
||||
plist_t features_node = plist_new_uint((uint64_t) 0x1E << 32 | 0x5A7FFFF7);
|
||||
plist_dict_set_item(r_node, "features", features_node);
|
||||
|
||||
plist_t name_node = plist_new_string(name);
|
||||
plist_dict_set_item(r_node, "name", name_node);
|
||||
|
||||
plist_t audio_formats_node = plist_new_array();
|
||||
plist_t audio_format_0_node = plist_new_dict();
|
||||
plist_t audio_format_0_type_node = plist_new_uint(100);
|
||||
plist_t audio_format_0_audio_input_formats_node = plist_new_uint(67108860);
|
||||
plist_t audio_format_0_audio_output_formats_node = plist_new_uint(67108860);
|
||||
plist_dict_set_item(audio_format_0_node, "type", audio_format_0_type_node);
|
||||
plist_dict_set_item(audio_format_0_node, "audioInputFormats", audio_format_0_audio_input_formats_node);
|
||||
plist_dict_set_item(audio_format_0_node, "audioOutputFormats", audio_format_0_audio_output_formats_node);
|
||||
plist_array_append_item(audio_formats_node, audio_format_0_node);
|
||||
plist_t audio_format_1_node = plist_new_dict();
|
||||
plist_t audio_format_1_type_node = plist_new_uint(101);
|
||||
plist_t audio_format_1_audio_input_formats_node = plist_new_uint(67108860);
|
||||
plist_t audio_format_1_audio_output_formats_node = plist_new_uint(67108860);
|
||||
plist_dict_set_item(audio_format_1_node, "type", audio_format_1_type_node);
|
||||
plist_dict_set_item(audio_format_1_node, "audioInputFormats", audio_format_1_audio_input_formats_node);
|
||||
plist_dict_set_item(audio_format_1_node, "audioOutputFormats", audio_format_1_audio_output_formats_node);
|
||||
plist_array_append_item(audio_formats_node, audio_format_1_node);
|
||||
plist_dict_set_item(r_node, "audioFormats", audio_formats_node);
|
||||
|
||||
plist_t pi_node = plist_new_string(AIRPLAY_PI);
|
||||
plist_dict_set_item(r_node, "pi", pi_node);
|
||||
|
||||
plist_t vv_node = plist_new_uint(strtol(AIRPLAY_VV, NULL, 10));
|
||||
plist_dict_set_item(r_node, "vv", vv_node);
|
||||
|
||||
plist_t status_flags_node = plist_new_uint(68);
|
||||
plist_dict_set_item(r_node, "statusFlags", status_flags_node);
|
||||
|
||||
plist_t keep_alive_low_power_node = plist_new_bool(1);
|
||||
plist_dict_set_item(r_node, "keepAliveLowPower", keep_alive_low_power_node);
|
||||
|
||||
plist_t source_version_node = plist_new_string(AIRPLAY_SRCVERS);
|
||||
plist_dict_set_item(r_node, "sourceVersion", source_version_node);
|
||||
|
||||
plist_t pk_node = plist_new_data(pk, pk_len);
|
||||
plist_dict_set_item(r_node, "pk", pk_node);
|
||||
|
||||
plist_t keep_alive_send_stats_as_body_node = plist_new_bool(1);
|
||||
plist_dict_set_item(r_node, "keepAliveSendStatsAsBody", keep_alive_send_stats_as_body_node);
|
||||
|
||||
plist_t device_id_node = plist_new_string(hw_addr);
|
||||
plist_dict_set_item(r_node, "deviceID", device_id_node);
|
||||
|
||||
plist_t audio_latencies_node = plist_new_array();
|
||||
plist_t audio_latencies_0_node = plist_new_dict();
|
||||
plist_t audio_latencies_0_output_latency_micros_node = plist_new_uint(0);
|
||||
plist_t audio_latencies_0_type_node = plist_new_uint(100);
|
||||
plist_t audio_latencies_0_audio_type_node = plist_new_string("default");
|
||||
plist_t audio_latencies_0_input_latency_micros_node = plist_new_uint(0);
|
||||
plist_dict_set_item(audio_latencies_0_node, "outputLatencyMicros", audio_latencies_0_output_latency_micros_node);
|
||||
plist_dict_set_item(audio_latencies_0_node, "type", audio_latencies_0_type_node);
|
||||
plist_dict_set_item(audio_latencies_0_node, "audioType", audio_latencies_0_audio_type_node);
|
||||
plist_dict_set_item(audio_latencies_0_node, "inputLatencyMicros", audio_latencies_0_input_latency_micros_node);
|
||||
plist_array_append_item(audio_latencies_node, audio_latencies_0_node);
|
||||
plist_t audio_latencies_1_node = plist_new_dict();
|
||||
plist_t audio_latencies_1_output_latency_micros_node = plist_new_uint(0);
|
||||
plist_t audio_latencies_1_type_node = plist_new_uint(101);
|
||||
plist_t audio_latencies_1_audio_type_node = plist_new_string("default");
|
||||
plist_t audio_latencies_1_input_latency_micros_node = plist_new_uint(0);
|
||||
plist_dict_set_item(audio_latencies_1_node, "outputLatencyMicros", audio_latencies_1_output_latency_micros_node);
|
||||
plist_dict_set_item(audio_latencies_1_node, "type", audio_latencies_1_type_node);
|
||||
plist_dict_set_item(audio_latencies_1_node, "audioType", audio_latencies_1_audio_type_node);
|
||||
plist_dict_set_item(audio_latencies_1_node, "inputLatencyMicros", audio_latencies_1_input_latency_micros_node);
|
||||
plist_array_append_item(audio_latencies_node, audio_latencies_1_node);
|
||||
plist_dict_set_item(r_node, "audioLatencies", audio_latencies_node);
|
||||
|
||||
plist_t model_node = plist_new_string(GLOBAL_MODEL);
|
||||
plist_dict_set_item(r_node, "model", model_node);
|
||||
|
||||
plist_t mac_address_node = plist_new_string(hw_addr);
|
||||
plist_dict_set_item(r_node, "macAddress", mac_address_node);
|
||||
|
||||
plist_t displays_node = plist_new_array();
|
||||
plist_t displays_0_node = plist_new_dict();
|
||||
plist_t displays_0_uuid_node = plist_new_string("e0ff8a27-6738-3d56-8a16-cc53aacee925");
|
||||
plist_t displays_0_width_physical_node = plist_new_uint(0);
|
||||
plist_t displays_0_height_physical_node = plist_new_uint(0);
|
||||
plist_t displays_0_width_node = plist_new_uint(1920);
|
||||
plist_t displays_0_height_node = plist_new_uint(1080);
|
||||
plist_t displays_0_width_pixels_node = plist_new_uint(1920);
|
||||
plist_t displays_0_height_pixels_node = plist_new_uint(1080);
|
||||
plist_t displays_0_rotation_node = plist_new_bool(0);
|
||||
plist_t displays_0_refresh_rate_node = plist_new_real(1.0 / 60.0);
|
||||
plist_t displays_0_overscanned_node = plist_new_bool(1);
|
||||
plist_t displays_0_features = plist_new_uint(14);
|
||||
|
||||
plist_dict_set_item(displays_0_node, "uuid", displays_0_uuid_node);
|
||||
plist_dict_set_item(displays_0_node, "widthPhysical", displays_0_width_physical_node);
|
||||
plist_dict_set_item(displays_0_node, "heightPhysical", displays_0_height_physical_node);
|
||||
plist_dict_set_item(displays_0_node, "width", displays_0_width_node);
|
||||
plist_dict_set_item(displays_0_node, "height", displays_0_height_node);
|
||||
plist_dict_set_item(displays_0_node, "widthPixels", displays_0_width_pixels_node);
|
||||
plist_dict_set_item(displays_0_node, "heightPixels", displays_0_height_pixels_node);
|
||||
plist_dict_set_item(displays_0_node, "rotation", displays_0_rotation_node);
|
||||
plist_dict_set_item(displays_0_node, "refreshRate", displays_0_refresh_rate_node);
|
||||
plist_dict_set_item(displays_0_node, "overscanned", displays_0_overscanned_node);
|
||||
plist_dict_set_item(displays_0_node, "features", displays_0_features);
|
||||
plist_array_append_item(displays_node, displays_0_node);
|
||||
plist_dict_set_item(r_node, "displays", displays_node);
|
||||
|
||||
plist_to_bin(r_node, response_data, (uint32_t *) response_datalen);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "INFO len = %d", response_datalen);
|
||||
http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist");
|
||||
free(pk);
|
||||
free(hw_addr);
|
||||
}
|
||||
|
||||
static void
|
||||
raop_handler_pairsetup(raop_conn_t *conn,
|
||||
http_request_t *request, http_response_t *response,
|
||||
char **response_data, int *response_datalen)
|
||||
{
|
||||
unsigned char public_key[32];
|
||||
const char *data;
|
||||
int datalen;
|
||||
|
||||
data = http_request_get_data(request, &datalen);
|
||||
if (datalen != 32) {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-setup data");
|
||||
return;
|
||||
}
|
||||
|
||||
pairing_get_public_key(conn->raop->pairing, public_key);
|
||||
pairing_session_set_setup_status(conn->pairing);
|
||||
|
||||
*response_data = malloc(sizeof(public_key));
|
||||
if (*response_data) {
|
||||
http_response_add_header(response, "Content-Type", "application/octet-stream");
|
||||
memcpy(*response_data, public_key, sizeof(public_key));
|
||||
*response_datalen = sizeof(public_key);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
raop_handler_pairverify(raop_conn_t *conn,
|
||||
http_request_t *request, http_response_t *response,
|
||||
char **response_data, int *response_datalen)
|
||||
{
|
||||
if (pairing_session_check_handshake_status(conn->pairing)) {
|
||||
return;
|
||||
}
|
||||
unsigned char public_key[32];
|
||||
unsigned char signature[64];
|
||||
const unsigned char *data;
|
||||
int datalen;
|
||||
|
||||
data = (unsigned char *) http_request_get_data(request, &datalen);
|
||||
if (datalen < 4) {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-verify data");
|
||||
return;
|
||||
}
|
||||
switch (data[0]) {
|
||||
case 1:
|
||||
if (datalen != 4 + 32 + 32) {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-verify data");
|
||||
return;
|
||||
}
|
||||
/* We can fall through these errors, the result will just be garbage... */
|
||||
if (pairing_session_handshake(conn->pairing, data + 4, data + 4 + 32)) {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Error initializing pair-verify handshake");
|
||||
}
|
||||
if (pairing_session_get_public_key(conn->pairing, public_key)) {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Error getting ECDH public key");
|
||||
}
|
||||
if (pairing_session_get_signature(conn->pairing, signature)) {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Error getting ED25519 signature");
|
||||
}
|
||||
*response_data = malloc(sizeof(public_key) + sizeof(signature));
|
||||
if (*response_data) {
|
||||
http_response_add_header(response, "Content-Type", "application/octet-stream");
|
||||
memcpy(*response_data, public_key, sizeof(public_key));
|
||||
memcpy(*response_data + sizeof(public_key), signature, sizeof(signature));
|
||||
*response_datalen = sizeof(public_key) + sizeof(signature);
|
||||
}
|
||||
break;
|
||||
case 0:
|
||||
if (datalen != 4 + 64) {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-verify data");
|
||||
return;
|
||||
}
|
||||
|
||||
if (pairing_session_finish(conn->pairing, data + 4)) {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Incorrect pair-verify signature");
|
||||
http_response_set_disconnect(response, 1);
|
||||
return;
|
||||
}
|
||||
http_response_add_header(response, "Content-Type", "application/octet-stream");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
raop_handler_fpsetup(raop_conn_t *conn,
|
||||
http_request_t *request, http_response_t *response,
|
||||
char **response_data, int *response_datalen)
|
||||
{
|
||||
const unsigned char *data;
|
||||
int datalen;
|
||||
|
||||
data = (unsigned char *) http_request_get_data(request, &datalen);
|
||||
if (datalen == 16) {
|
||||
*response_data = malloc(142);
|
||||
if (*response_data) {
|
||||
http_response_add_header(response, "Content-Type", "application/octet-stream");
|
||||
if (!fairplay_setup(conn->fairplay, data, (unsigned char *) *response_data)) {
|
||||
*response_datalen = 142;
|
||||
} else {
|
||||
// Handle error?
|
||||
free(*response_data);
|
||||
*response_data = NULL;
|
||||
}
|
||||
}
|
||||
} else if (datalen == 164) {
|
||||
*response_data = malloc(32);
|
||||
if (*response_data) {
|
||||
http_response_add_header(response, "Content-Type", "application/octet-stream");
|
||||
if (!fairplay_handshake(conn->fairplay, data, (unsigned char *) *response_data)) {
|
||||
*response_datalen = 32;
|
||||
} else {
|
||||
// Handle error?
|
||||
free(*response_data);
|
||||
*response_data = NULL;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Invalid fp-setup data length");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
raop_handler_options(raop_conn_t *conn,
|
||||
http_request_t *request, http_response_t *response,
|
||||
char **response_data, int *response_datalen)
|
||||
{
|
||||
http_response_add_header(response, "Public", "SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER");
|
||||
}
|
||||
|
||||
static void
|
||||
raop_handler_setup(raop_conn_t *conn,
|
||||
http_request_t *request, http_response_t *response,
|
||||
char **response_data, int *response_datalen)
|
||||
{
|
||||
unsigned short remote_cport = 0;
|
||||
|
||||
const char *transport;
|
||||
int use_udp;
|
||||
const char *dacp_id;
|
||||
const char *active_remote_header;
|
||||
|
||||
const char *data;
|
||||
int data_len;
|
||||
|
||||
data = http_request_get_data(request, &data_len);
|
||||
|
||||
dacp_id = http_request_get_header(request, "DACP-ID");
|
||||
active_remote_header = http_request_get_header(request, "Active-Remote");
|
||||
|
||||
if (dacp_id && active_remote_header) {
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "DACP-ID: %s", dacp_id);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "Active-Remote: %s", active_remote_header);
|
||||
if (conn->raop_rtp) {
|
||||
raop_rtp_remote_control_id(conn->raop_rtp, dacp_id, active_remote_header);
|
||||
}
|
||||
}
|
||||
|
||||
transport = http_request_get_header(request, "Transport");
|
||||
if (transport) {
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "Transport: %s", transport);
|
||||
use_udp = strncmp(transport, "RTP/AVP/TCP", 11);
|
||||
} else {
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "Transport: null");
|
||||
use_udp = 0;
|
||||
}
|
||||
|
||||
// Parsing bplist
|
||||
plist_t req_root_node = NULL;
|
||||
plist_from_bin(data, data_len, &req_root_node);
|
||||
plist_t req_streams_node = plist_dict_get_item(req_root_node, "streams");
|
||||
plist_t req_eiv_node = plist_dict_get_item(req_root_node, "eiv");
|
||||
plist_t req_ekey_node = plist_dict_get_item(req_root_node, "ekey");
|
||||
|
||||
// For the response
|
||||
plist_t res_root_node = plist_new_dict();
|
||||
|
||||
if (PLIST_IS_DATA(req_eiv_node) && PLIST_IS_DATA(req_ekey_node)) {
|
||||
// The first SETUP call that initializes keys and timing
|
||||
|
||||
unsigned char aesiv[16];
|
||||
unsigned char aeskey[16];
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "SETUP 1");
|
||||
|
||||
// First setup
|
||||
char* eiv = NULL;
|
||||
uint64_t eiv_len = 0;
|
||||
plist_get_data_val(req_eiv_node, &eiv, &eiv_len);
|
||||
memcpy(aesiv, eiv, 16);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "eiv_len = %llu", eiv_len);
|
||||
char* ekey = NULL;
|
||||
uint64_t ekey_len = 0;
|
||||
plist_get_data_val(req_ekey_node, &ekey, &ekey_len);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "ekey_len = %llu", ekey_len);
|
||||
|
||||
// ekey is 72 bytes, aeskey is 16 bytes
|
||||
int ret = fairplay_decrypt(conn->fairplay, (unsigned char*) ekey, aeskey);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "fairplay_decrypt ret = %d", ret);
|
||||
unsigned char ecdh_secret[32];
|
||||
pairing_get_ecdh_secret_key(conn->pairing, ecdh_secret);
|
||||
|
||||
// Time port
|
||||
uint64_t timing_rport;
|
||||
plist_t time_note = plist_dict_get_item(req_root_node, "timingPort");
|
||||
plist_get_uint_val(time_note, &timing_rport);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "timing_rport = %llu", timing_rport);
|
||||
|
||||
unsigned short timing_lport;
|
||||
conn->raop_ntp = raop_ntp_init(conn->raop->logger, conn->remote, conn->remotelen, timing_rport);
|
||||
raop_ntp_start(conn->raop_ntp, &timing_lport);
|
||||
|
||||
conn->raop_rtp = raop_rtp_init(conn->raop->logger, &conn->raop->callbacks, conn->raop_ntp, conn->remote, conn->remotelen, aeskey, aesiv, ecdh_secret);
|
||||
conn->raop_rtp_mirror = raop_rtp_mirror_init(conn->raop->logger, &conn->raop->callbacks, conn->raop_ntp, conn->remote, conn->remotelen, aeskey, ecdh_secret);
|
||||
|
||||
plist_t res_event_port_node = plist_new_uint(conn->raop->port);
|
||||
plist_t res_timing_port_node = plist_new_uint(timing_lport);
|
||||
plist_dict_set_item(res_root_node, "timingPort", res_timing_port_node);
|
||||
plist_dict_set_item(res_root_node, "eventPort", res_event_port_node);
|
||||
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "eport = %d, tport = %d", conn->raop->port, timing_lport);
|
||||
}
|
||||
|
||||
// Process stream setup requests
|
||||
if (PLIST_IS_ARRAY(req_streams_node)) {
|
||||
plist_t res_streams_node = plist_new_array();
|
||||
|
||||
int count = plist_array_get_size(req_streams_node);
|
||||
for (int i = 0; i < count; i++) {
|
||||
plist_t req_stream_node = plist_array_get_item(req_streams_node, i);
|
||||
plist_t req_stream_type_node = plist_dict_get_item(req_stream_node, "type");
|
||||
uint64_t type;
|
||||
plist_get_uint_val(req_stream_type_node, &type);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "type = %llu", type);
|
||||
|
||||
switch (type) {
|
||||
case 110: {
|
||||
// Mirroring
|
||||
unsigned short dport = 0;
|
||||
plist_t stream_id_node = plist_dict_get_item(req_stream_node, "streamConnectionID");
|
||||
uint64_t stream_connection_id;
|
||||
plist_get_uint_val(stream_id_node, &stream_connection_id);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "streamConnectionID = %llu", stream_connection_id);
|
||||
|
||||
if (conn->raop_rtp_mirror) {
|
||||
raop_rtp_init_mirror_aes(conn->raop_rtp_mirror, stream_connection_id);
|
||||
raop_rtp_start_mirror(conn->raop_rtp_mirror, use_udp, &dport);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "Mirroring initialized successfully");
|
||||
} else {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "Mirroring not initialized at SETUP, playing will fail!");
|
||||
http_response_set_disconnect(response, 1);
|
||||
}
|
||||
|
||||
plist_t res_stream_node = plist_new_dict();
|
||||
plist_t res_stream_data_port_node = plist_new_uint(dport);
|
||||
plist_t res_stream_type_node = plist_new_uint(110);
|
||||
plist_dict_set_item(res_stream_node, "dataPort", res_stream_data_port_node);
|
||||
plist_dict_set_item(res_stream_node, "type", res_stream_type_node);
|
||||
plist_array_append_item(res_streams_node, res_stream_node);
|
||||
|
||||
break;
|
||||
} case 96: {
|
||||
// Audio
|
||||
|
||||
unsigned short cport = 0, dport = 0;
|
||||
|
||||
if (conn->raop_rtp) {
|
||||
raop_rtp_start_audio(conn->raop_rtp, use_udp, remote_cport, &cport, &dport);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "RAOP initialized success");
|
||||
} else {
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "RAOP not initialized at SETUP, playing will fail!");
|
||||
http_response_set_disconnect(response, 1);
|
||||
}
|
||||
|
||||
plist_t res_stream_node = plist_new_dict();
|
||||
plist_t res_stream_data_port_node = plist_new_uint(dport);
|
||||
plist_t res_stream_control_port_node = plist_new_uint(cport);
|
||||
plist_t res_stream_type_node = plist_new_uint(96);
|
||||
plist_dict_set_item(res_stream_node, "dataPort", res_stream_data_port_node);
|
||||
plist_dict_set_item(res_stream_node, "controlPort", res_stream_control_port_node);
|
||||
plist_dict_set_item(res_stream_node, "type", res_stream_type_node);
|
||||
plist_array_append_item(res_streams_node, res_stream_node);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
logger_log(conn->raop->logger, LOGGER_ERR, "SETUP tries to setup stream of unknown type %llu", type);
|
||||
http_response_set_disconnect(response, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
plist_dict_set_item(res_root_node, "streams", res_streams_node);
|
||||
}
|
||||
|
||||
plist_to_bin(res_root_node, response_data, (uint32_t*) response_datalen);
|
||||
http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist");
|
||||
}
|
||||
|
||||
static void
|
||||
raop_handler_get_parameter(raop_conn_t *conn,
|
||||
http_request_t *request, http_response_t *response,
|
||||
char **response_data, int *response_datalen)
|
||||
{
|
||||
const char *content_type;
|
||||
const char *data;
|
||||
int datalen;
|
||||
|
||||
content_type = http_request_get_header(request, "Content-Type");
|
||||
data = http_request_get_data(request, &datalen);
|
||||
if (!strcmp(content_type, "text/parameters")) {
|
||||
const char *current = data;
|
||||
|
||||
while (current) {
|
||||
const char *next;
|
||||
int handled = 0;
|
||||
|
||||
/* This is a bit ugly, but seems to be how airport works too */
|
||||
if (!strncmp(current, "volume\r\n", 8)) {
|
||||
const char volume[] = "volume: 0.0\r\n";
|
||||
|
||||
http_response_add_header(response, "Content-Type", "text/parameters");
|
||||
*response_data = strdup(volume);
|
||||
if (*response_data) {
|
||||
*response_datalen = strlen(*response_data);
|
||||
}
|
||||
handled = 1;
|
||||
}
|
||||
|
||||
next = strstr(current, "\r\n");
|
||||
if (next && !handled) {
|
||||
logger_log(conn->raop->logger, LOGGER_WARNING,
|
||||
"Found an unknown parameter: %.*s", (next - current), current);
|
||||
current = next + 2;
|
||||
} else if (next) {
|
||||
current = next + 2;
|
||||
} else {
|
||||
current = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
raop_handler_set_parameter(raop_conn_t *conn,
|
||||
http_request_t *request, http_response_t *response,
|
||||
char **response_data, int *response_datalen)
|
||||
{
|
||||
const char *content_type;
|
||||
const char *data;
|
||||
int datalen;
|
||||
|
||||
content_type = http_request_get_header(request, "Content-Type");
|
||||
data = http_request_get_data(request, &datalen);
|
||||
if (!strcmp(content_type, "text/parameters")) {
|
||||
char *datastr;
|
||||
datastr = calloc(1, datalen+1);
|
||||
if (data && datastr && conn->raop_rtp) {
|
||||
memcpy(datastr, data, datalen);
|
||||
if (!strncmp(datastr, "volume: ", 8)) {
|
||||
float vol = 0.0;
|
||||
sscanf(datastr+8, "%f", &vol);
|
||||
raop_rtp_set_volume(conn->raop_rtp, vol);
|
||||
} else if (!strncmp(datastr, "progress: ", 10)) {
|
||||
unsigned int start, curr, end;
|
||||
sscanf(datastr+10, "%u/%u/%u", &start, &curr, &end);
|
||||
raop_rtp_set_progress(conn->raop_rtp, start, curr, end);
|
||||
}
|
||||
} else if (!conn->raop_rtp) {
|
||||
logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER");
|
||||
}
|
||||
free(datastr);
|
||||
} else if (!strcmp(content_type, "image/jpeg") || !strcmp(content_type, "image/png")) {
|
||||
logger_log(conn->raop->logger, LOGGER_INFO, "Got image data of %d bytes", datalen);
|
||||
if (conn->raop_rtp) {
|
||||
raop_rtp_set_coverart(conn->raop_rtp, data, datalen);
|
||||
} else {
|
||||
logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER coverart");
|
||||
}
|
||||
} else if (!strcmp(content_type, "application/x-dmap-tagged")) {
|
||||
logger_log(conn->raop->logger, LOGGER_INFO, "Got metadata of %d bytes", datalen);
|
||||
if (conn->raop_rtp) {
|
||||
raop_rtp_set_metadata(conn->raop_rtp, data, datalen);
|
||||
} else {
|
||||
logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER metadata");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
raop_handler_feedback(raop_conn_t *conn,
|
||||
http_request_t *request, http_response_t *response,
|
||||
char **response_data, int *response_datalen)
|
||||
{
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "raop_handler_feedback");
|
||||
}
|
||||
|
||||
static void
|
||||
raop_handler_record(raop_conn_t *conn,
|
||||
http_request_t *request, http_response_t *response,
|
||||
char **response_data, int *response_datalen)
|
||||
{
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "raop_handler_record");
|
||||
http_response_add_header(response, "Audio-Latency", "11025");
|
||||
http_response_add_header(response, "Audio-Jack-Status", "connected; type=analog");
|
||||
}
|
||||
446
lib/raop_ntp.c
Normal file
446
lib/raop_ntp.c
Normal file
@@ -0,0 +1,446 @@
|
||||
/*
|
||||
* Copyright (c) 2019 dsafa22 and 2014 Joakim Plate, modified by Florian Draschbacher,
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// Some of the code in here comes from https://github.com/juhovh/shairplay/pull/25/files
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "raop_ntp.h"
|
||||
#include "threads.h"
|
||||
#include "compat.h"
|
||||
#include "netutils.h"
|
||||
#include "byteutils.h"
|
||||
|
||||
#define RAOP_NTP_DATA_COUNT 8
|
||||
#define RAOP_NTP_PHI_PPM 15ull // PPM
|
||||
#define RAOP_NTP_R_RHO ((1ull << 32) / 1000u) // packet precision
|
||||
#define RAOP_NTP_S_RHO ((1ull << 32) / 1000u) // system clock precision
|
||||
#define RAOP_NTP_MAX_DIST ((1500ull << 32) / 1000u) // maximum allowed distance
|
||||
#define RAOP_NTP_MAX_DISP ((16ull << 32)) // maximum dispersion
|
||||
|
||||
#define RAOP_NTP_CLOCK_BASE (2208988800ull << 32)
|
||||
|
||||
typedef struct raop_ntp_data_s {
|
||||
uint64_t time; // The local wall clock time at time of ntp packet arrival
|
||||
uint64_t dispersion;
|
||||
int64_t delay; // The round trip delay
|
||||
int64_t offset; // The difference between remote and local wall clock time
|
||||
} raop_ntp_data_t;
|
||||
|
||||
struct raop_ntp_s {
|
||||
logger_t *logger;
|
||||
|
||||
thread_handle_t thread;
|
||||
mutex_handle_t run_mutex;
|
||||
|
||||
mutex_handle_t wait_mutex;
|
||||
cond_handle_t wait_cond;
|
||||
|
||||
raop_ntp_data_t data[RAOP_NTP_DATA_COUNT];
|
||||
int data_index;
|
||||
|
||||
// The clock sync params are periodically updated to the AirPlay client's NTP clock
|
||||
mutex_handle_t sync_params_mutex;
|
||||
int64_t sync_offset;
|
||||
int64_t sync_dispersion;
|
||||
int64_t sync_delay;
|
||||
|
||||
// Socket address of the AirPlay client
|
||||
struct sockaddr_storage remote_saddr;
|
||||
socklen_t remote_saddr_len;
|
||||
|
||||
// The remote port of the NTP server on the AirPlay client
|
||||
unsigned short timing_rport;
|
||||
|
||||
// The local port of the NTP client on the AirPlay server
|
||||
unsigned short timing_lport;
|
||||
|
||||
/* MUTEX LOCKED VARIABLES START */
|
||||
/* These variables only edited mutex locked */
|
||||
int running;
|
||||
int joined;
|
||||
|
||||
// UDP socket
|
||||
int tsock;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Used for sorting the data array by delay
|
||||
*/
|
||||
static int
|
||||
raop_ntp_compare(const void* av, const void* bv)
|
||||
{
|
||||
const raop_ntp_data_t* a = (const raop_ntp_data_t*)av;
|
||||
const raop_ntp_data_t* b = (const raop_ntp_data_t*)bv;
|
||||
if (a->delay < b->delay) {
|
||||
return -1;
|
||||
} else if(a->delay > b->delay) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
raop_ntp_parse_remote_address(raop_ntp_t *raop_ntp, const unsigned char *remote_addr, int remote_addr_len)
|
||||
{
|
||||
char current[25];
|
||||
int family;
|
||||
int ret;
|
||||
assert(raop_ntp);
|
||||
if (remote_addr_len == 4) {
|
||||
family = AF_INET;
|
||||
} else if (remote_addr_len == 16) {
|
||||
family = AF_INET6;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
memset(current, 0, sizeof(current));
|
||||
sprintf(current, "%d.%d.%d.%d", remote_addr[0], remote_addr[1], remote_addr[2], remote_addr[3]);
|
||||
logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp parse remote ip = %s", current);
|
||||
ret = netutils_parse_address(family, current,
|
||||
&raop_ntp->remote_saddr,
|
||||
sizeof(raop_ntp->remote_saddr));
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
raop_ntp->remote_saddr_len = ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
raop_ntp_t *raop_ntp_init(logger_t *logger, const unsigned char *remote_addr, int remote_addr_len, unsigned short timing_rport) {
|
||||
raop_ntp_t *raop_ntp;
|
||||
|
||||
assert(logger);
|
||||
|
||||
raop_ntp = calloc(1, sizeof(raop_ntp_t));
|
||||
if (!raop_ntp) {
|
||||
return NULL;
|
||||
}
|
||||
raop_ntp->logger = logger;
|
||||
raop_ntp->timing_rport = timing_rport;
|
||||
|
||||
if (raop_ntp_parse_remote_address(raop_ntp, remote_addr, remote_addr_len) < 0) {
|
||||
free(raop_ntp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Set port on the remote address struct
|
||||
((struct sockaddr_in *) &raop_ntp->remote_saddr)->sin_port = htons(timing_rport);
|
||||
|
||||
raop_ntp->running = 0;
|
||||
raop_ntp->joined = 1;
|
||||
|
||||
uint64_t time = raop_ntp_get_local_time(raop_ntp);
|
||||
|
||||
for (int i = 0; i < RAOP_NTP_DATA_COUNT; ++i) {
|
||||
raop_ntp->data[i].offset = 0ll;
|
||||
raop_ntp->data[i].delay = RAOP_NTP_MAX_DISP;
|
||||
raop_ntp->data[i].dispersion = RAOP_NTP_MAX_DISP;
|
||||
raop_ntp->data[i].time = time;
|
||||
}
|
||||
|
||||
raop_ntp->sync_delay = 0;
|
||||
raop_ntp->sync_dispersion = 0;
|
||||
raop_ntp->sync_offset = 0;
|
||||
|
||||
MUTEX_CREATE(raop_ntp->run_mutex);
|
||||
MUTEX_CREATE(raop_ntp->wait_mutex);
|
||||
COND_CREATE(raop_ntp->wait_cond);
|
||||
MUTEX_CREATE(raop_ntp->sync_params_mutex);
|
||||
return raop_ntp;
|
||||
}
|
||||
|
||||
void
|
||||
raop_ntp_destroy(raop_ntp_t *raop_ntp)
|
||||
{
|
||||
if (raop_ntp) {
|
||||
raop_ntp_stop(raop_ntp);
|
||||
MUTEX_DESTROY(raop_ntp->run_mutex);
|
||||
MUTEX_DESTROY(raop_ntp->wait_mutex);
|
||||
COND_DESTROY(raop_ntp->wait_cond);
|
||||
MUTEX_DESTROY(raop_ntp->sync_params_mutex);
|
||||
free(raop_ntp);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned short raop_ntp_get_port(raop_ntp_t *raop_ntp) {
|
||||
return raop_ntp->timing_lport;
|
||||
}
|
||||
|
||||
static int
|
||||
raop_ntp_init_socket(raop_ntp_t *raop_ntp, int use_ipv6)
|
||||
{
|
||||
int tsock = -1;
|
||||
unsigned short tport = 0;
|
||||
|
||||
assert(raop_ntp);
|
||||
|
||||
tsock = netutils_init_socket(&tport, use_ipv6, 1);
|
||||
|
||||
if (tsock == -1) {
|
||||
goto sockets_cleanup;
|
||||
}
|
||||
|
||||
// We're calling recvfrom without knowing whether there is any data, so we need a timeout
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 3000;
|
||||
if (setsockopt(tsock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
|
||||
goto sockets_cleanup;
|
||||
}
|
||||
|
||||
/* Set socket descriptors */
|
||||
raop_ntp->tsock = tsock;
|
||||
|
||||
/* Set port values */
|
||||
raop_ntp->timing_lport = tport;
|
||||
return 0;
|
||||
|
||||
sockets_cleanup:
|
||||
if (tsock != -1) closesocket(tsock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static THREAD_RETVAL
|
||||
raop_ntp_thread(void *arg)
|
||||
{
|
||||
raop_ntp_t *raop_ntp = arg;
|
||||
assert(raop_ntp);
|
||||
unsigned char response[128];
|
||||
int response_len;
|
||||
unsigned char request[32] = {0x80, 0xd2, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
raop_ntp_data_t data_sorted[RAOP_NTP_DATA_COUNT];
|
||||
const unsigned two_pow_n[RAOP_NTP_DATA_COUNT] = {2, 4, 8, 16, 32, 64, 128, 256};
|
||||
|
||||
while (1) {
|
||||
MUTEX_LOCK(raop_ntp->run_mutex);
|
||||
if (!raop_ntp->running) {
|
||||
MUTEX_UNLOCK(raop_ntp->run_mutex);
|
||||
break;
|
||||
}
|
||||
MUTEX_UNLOCK(raop_ntp->run_mutex);
|
||||
|
||||
// Send request
|
||||
uint64_t send_time = raop_ntp_get_local_time(raop_ntp);
|
||||
byteutils_put_ntp_timestamp(request, 24, send_time);
|
||||
int send_len = sendto(raop_ntp->tsock, (char *)request, sizeof(request), 0,
|
||||
(struct sockaddr *) &raop_ntp->remote_saddr, raop_ntp->remote_saddr_len);
|
||||
logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp send_len = %d", send_len);
|
||||
if (send_len < 0) {
|
||||
logger_log(raop_ntp->logger, LOGGER_ERR, "raop_ntp error sending request");
|
||||
break;
|
||||
}
|
||||
|
||||
// Read response
|
||||
response_len = recvfrom(raop_ntp->tsock, (char *)response, sizeof(response), 0,
|
||||
(struct sockaddr *) &raop_ntp->remote_saddr, &raop_ntp->remote_saddr_len);
|
||||
if (response_len < 0) {
|
||||
logger_log(raop_ntp->logger, LOGGER_ERR, "raop_ntp receive timeout");
|
||||
break;
|
||||
}
|
||||
logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp receive time type_t packetlen = %d", response_len);
|
||||
|
||||
int64_t t3 = (int64_t) raop_ntp_get_local_time(raop_ntp);
|
||||
// Local time of the client when the NTP request packet leaves the client
|
||||
int64_t t0 = (int64_t) byteutils_get_ntp_timestamp(response, 8);
|
||||
// Local time of the server when the NTP request packet arrives at the server
|
||||
int64_t t1 = (int64_t) byteutils_get_ntp_timestamp(response, 16);
|
||||
// Local time of the server when the response message leaves the server
|
||||
int64_t t2 = (int64_t) byteutils_get_ntp_timestamp(response, 24);
|
||||
|
||||
// The iOS device sends its time in micro seconds relative to an arbitrary Epoch (the last boot).
|
||||
// For a little bonus confusion, they add SECONDS_FROM_1900_TO_1970 * 1000000 us.
|
||||
// This means we have to expect some rather huge offset, but its growth or shrink over time should be small.
|
||||
|
||||
raop_ntp->data_index = (raop_ntp->data_index + 1) % RAOP_NTP_DATA_COUNT;
|
||||
raop_ntp->data[raop_ntp->data_index].time = t3;
|
||||
raop_ntp->data[raop_ntp->data_index].offset = ((t1 - t0) + (t2 - t3)) / 2;
|
||||
raop_ntp->data[raop_ntp->data_index].delay = ((t3 - t0) - (t2 - t1));
|
||||
raop_ntp->data[raop_ntp->data_index].dispersion = RAOP_NTP_R_RHO + RAOP_NTP_S_RHO + (t3 - t0) * RAOP_NTP_PHI_PPM / 1000000u;
|
||||
|
||||
// Sort by delay
|
||||
memcpy(data_sorted, raop_ntp->data, sizeof(data_sorted));
|
||||
qsort(data_sorted, RAOP_NTP_DATA_COUNT, sizeof(data_sorted[0]), raop_ntp_compare);
|
||||
|
||||
uint64_t dispersion = 0ull;
|
||||
int64_t offset = data_sorted[0].offset;
|
||||
int64_t delay = data_sorted[RAOP_NTP_DATA_COUNT - 1].delay;
|
||||
|
||||
// Calculate dispersion
|
||||
for(int i = 0; i < RAOP_NTP_DATA_COUNT; ++i) {
|
||||
unsigned long long disp = raop_ntp->data[i].dispersion + (t3 - raop_ntp->data[i].time) * RAOP_NTP_PHI_PPM / 1000000u;
|
||||
dispersion += disp / two_pow_n[i];
|
||||
}
|
||||
|
||||
MUTEX_LOCK(raop_ntp->sync_params_mutex);
|
||||
|
||||
int64_t correction = offset - raop_ntp->sync_offset;
|
||||
raop_ntp->sync_offset = offset;
|
||||
raop_ntp->sync_dispersion = dispersion;
|
||||
raop_ntp->sync_delay = delay;
|
||||
MUTEX_UNLOCK(raop_ntp->sync_params_mutex);
|
||||
|
||||
logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp sync correction = %lld", correction);
|
||||
|
||||
// Sleep for 3 seconds
|
||||
struct timeval now;
|
||||
struct timespec wait_time;
|
||||
MUTEX_LOCK(raop_ntp->wait_mutex);
|
||||
gettimeofday(&now, NULL);
|
||||
wait_time.tv_sec = now.tv_sec + 3;
|
||||
wait_time.tv_nsec = now.tv_usec * 1000;
|
||||
pthread_cond_timedwait(&raop_ntp->wait_cond, &raop_ntp->wait_mutex, &wait_time);
|
||||
MUTEX_UNLOCK(raop_ntp->wait_mutex);
|
||||
}
|
||||
|
||||
// Ensure running reflects the actual state
|
||||
MUTEX_LOCK(raop_ntp->run_mutex);
|
||||
raop_ntp->running = false;
|
||||
MUTEX_UNLOCK(raop_ntp->run_mutex);
|
||||
|
||||
logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp exiting thread");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
raop_ntp_start(raop_ntp_t *raop_ntp, unsigned short *timing_lport)
|
||||
{
|
||||
logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp starting time");
|
||||
int use_ipv6 = 0;
|
||||
|
||||
assert(raop_ntp);
|
||||
|
||||
MUTEX_LOCK(raop_ntp->run_mutex);
|
||||
if (raop_ntp->running || !raop_ntp->joined) {
|
||||
MUTEX_UNLOCK(raop_ntp->run_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Initialize ports and sockets */
|
||||
if (raop_ntp->remote_saddr.ss_family == AF_INET6) {
|
||||
use_ipv6 = 1;
|
||||
}
|
||||
use_ipv6 = 0;
|
||||
if (raop_ntp_init_socket(raop_ntp, use_ipv6) < 0) {
|
||||
logger_log(raop_ntp->logger, LOGGER_ERR, "raop_ntp initializing timing socket failed");
|
||||
MUTEX_UNLOCK(raop_ntp->run_mutex);
|
||||
return;
|
||||
}
|
||||
if (timing_lport) *timing_lport = raop_ntp->timing_lport;
|
||||
|
||||
/* Create the thread and initialize running values */
|
||||
raop_ntp->running = 1;
|
||||
raop_ntp->joined = 0;
|
||||
|
||||
THREAD_CREATE(raop_ntp->thread, raop_ntp_thread, raop_ntp);
|
||||
MUTEX_UNLOCK(raop_ntp->run_mutex);
|
||||
}
|
||||
|
||||
void
|
||||
raop_ntp_stop(raop_ntp_t *raop_ntp)
|
||||
{
|
||||
assert(raop_ntp);
|
||||
|
||||
/* Check that we are running and thread is not
|
||||
* joined (should never be while still running) */
|
||||
MUTEX_LOCK(raop_ntp->run_mutex);
|
||||
if (!raop_ntp->running || raop_ntp->joined) {
|
||||
MUTEX_UNLOCK(raop_ntp->run_mutex);
|
||||
return;
|
||||
}
|
||||
raop_ntp->running = 0;
|
||||
MUTEX_UNLOCK(raop_ntp->run_mutex);
|
||||
|
||||
logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp stopping time thread");
|
||||
|
||||
MUTEX_LOCK(raop_ntp->wait_mutex);
|
||||
COND_SIGNAL(raop_ntp->wait_cond);
|
||||
MUTEX_UNLOCK(raop_ntp->wait_mutex);
|
||||
|
||||
if (raop_ntp->tsock != -1) {
|
||||
closesocket(raop_ntp->tsock);
|
||||
raop_ntp->tsock = -1;
|
||||
}
|
||||
|
||||
THREAD_JOIN(raop_ntp->thread);
|
||||
|
||||
logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp stopped time thread");
|
||||
|
||||
/* Mark thread as joined */
|
||||
MUTEX_LOCK(raop_ntp->run_mutex);
|
||||
raop_ntp->joined = 1;
|
||||
MUTEX_UNLOCK(raop_ntp->run_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts from a little endian ntp timestamp to micro seconds since the Unix epoch.
|
||||
* Does the same thing as byteutils_get_ntp_timestamp, except its input is an uint64_t
|
||||
* and expected to already be in little endian.
|
||||
* Please note this just converts to a different representation, the clock remains the
|
||||
* same.
|
||||
*/
|
||||
uint64_t raop_ntp_timestamp_to_micro_seconds(uint64_t ntp_timestamp, bool account_for_epoch_diff) {
|
||||
uint64_t seconds = ((ntp_timestamp >> 32) & 0xffffffff) - (account_for_epoch_diff ? SECONDS_FROM_1900_TO_1970 : 0);
|
||||
uint64_t fraction = (ntp_timestamp & 0xffffffff);
|
||||
return (seconds * 1000000) + ((fraction * 1000000) >> 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current time in micro seconds according to the local wall clock.
|
||||
* The system Unix time is used as the local wall clock.
|
||||
*/
|
||||
uint64_t raop_ntp_get_local_time(raop_ntp_t *raop_ntp) {
|
||||
struct timespec time;
|
||||
clock_gettime(CLOCK_REALTIME, &time);
|
||||
return (uint64_t)time.tv_sec * 1000000L + (uint64_t)(time.tv_nsec / 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current time in micro seconds according to the remote wall clock.
|
||||
*/
|
||||
uint64_t raop_ntp_get_remote_time(raop_ntp_t *raop_ntp) {
|
||||
MUTEX_LOCK(raop_ntp->sync_params_mutex);
|
||||
int64_t offset = raop_ntp->sync_offset;
|
||||
MUTEX_UNLOCK(raop_ntp->sync_params_mutex);
|
||||
return (uint64_t) ((int64_t) raop_ntp_get_local_time(raop_ntp)) + ((int64_t) offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the local wall clock time in micro seconds for the given point in remote clock time
|
||||
*/
|
||||
uint64_t raop_ntp_convert_remote_time(raop_ntp_t *raop_ntp, uint64_t remote_time) {
|
||||
MUTEX_LOCK(raop_ntp->sync_params_mutex);
|
||||
uint64_t offset = raop_ntp->sync_offset;
|
||||
MUTEX_UNLOCK(raop_ntp->sync_params_mutex);
|
||||
return (uint64_t) ((int64_t) remote_time) - ((int64_t) offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the remote wall clock time in micro seconds for the given point in local clock time
|
||||
*/
|
||||
uint64_t raop_ntp_convert_local_time(raop_ntp_t *raop_ntp, uint64_t local_time) {
|
||||
MUTEX_LOCK(raop_ntp->sync_params_mutex);
|
||||
uint64_t offset = raop_ntp->sync_offset;
|
||||
MUTEX_UNLOCK(raop_ntp->sync_params_mutex);
|
||||
return (uint64_t) ((int64_t) local_time) + ((int64_t) offset);
|
||||
}
|
||||
42
lib/raop_ntp.h
Normal file
42
lib/raop_ntp.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2019 dsafa22, modified by Florian Draschbacher,
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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 RAOP_NTP_H
|
||||
#define RAOP_NTP_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "logger.h"
|
||||
|
||||
typedef struct raop_ntp_s raop_ntp_t;
|
||||
|
||||
raop_ntp_t *raop_ntp_init(logger_t *logger, const unsigned char *remote_addr, int remote_addr_len, unsigned short timing_rport);
|
||||
|
||||
void raop_ntp_start(raop_ntp_t *raop_ntp, unsigned short *timing_lport);
|
||||
|
||||
void raop_ntp_stop(raop_ntp_t *raop_ntp);
|
||||
|
||||
unsigned short raop_ntp_get_port(raop_ntp_t *raop_ntp);
|
||||
|
||||
void raop_ntp_destroy(raop_ntp_t *raop_rtp);
|
||||
|
||||
uint64_t raop_ntp_timestamp_to_micro_seconds(uint64_t ntp_timestamp, bool account_for_epoch_diff);
|
||||
|
||||
uint64_t raop_ntp_get_local_time(raop_ntp_t *raop_ntp);
|
||||
uint64_t raop_ntp_get_remote_time(raop_ntp_t *raop_ntp);
|
||||
uint64_t raop_ntp_convert_remote_time(raop_ntp_t *raop_ntp, uint64_t remote_time);
|
||||
uint64_t raop_ntp_convert_local_time(raop_ntp_t *raop_ntp, uint64_t local_time);
|
||||
|
||||
#endif //RAOP_NTP_H
|
||||
699
lib/raop_rtp.c
Executable file
699
lib/raop_rtp.c
Executable file
@@ -0,0 +1,699 @@
|
||||
/**
|
||||
* 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "raop_rtp.h"
|
||||
#include "raop.h"
|
||||
#include "raop_buffer.h"
|
||||
#include "netutils.h"
|
||||
#include "compat.h"
|
||||
#include "logger.h"
|
||||
#include "byteutils.h"
|
||||
#include "mirror_buffer.h"
|
||||
#include "stream.h"
|
||||
|
||||
#define NO_FLUSH (-42)
|
||||
|
||||
#define RAOP_RTP_SAMPLE_RATE (44100.0 / 1000000.0)
|
||||
#define RAOP_RTP_SYNC_DATA_COUNT 8
|
||||
|
||||
typedef struct raop_rtp_sync_data_s {
|
||||
uint64_t ntp_time; // The local wall clock time at the time of rtp_time
|
||||
uint32_t rtp_time; // The remote rtp clock time corresponding to ntp_time
|
||||
} raop_rtp_sync_data_t;
|
||||
|
||||
struct raop_rtp_s {
|
||||
logger_t *logger;
|
||||
raop_callbacks_t callbacks;
|
||||
|
||||
// Time and sync
|
||||
raop_ntp_t *ntp;
|
||||
double rtp_sync_scale;
|
||||
int64_t rtp_sync_offset;
|
||||
raop_rtp_sync_data_t sync_data[RAOP_RTP_SYNC_DATA_COUNT];
|
||||
int sync_data_index;
|
||||
|
||||
// Transmission Stats, could be used if a playout buffer is needed
|
||||
// float interarrival_jitter; // As defined by RTP RFC 3550, Section 6.4.1
|
||||
// unsigned int last_packet_transit_time;
|
||||
//int transit = (packet_receive_time - packet_send_time);
|
||||
// int d = transit - last_packet_transit_time;
|
||||
// if (d < 0) d = -d;
|
||||
// interarrival_jitter = (1.f / 16.f) * ((double) d - interarrival_jitter);
|
||||
|
||||
/* Buffer to handle all resends */
|
||||
raop_buffer_t *buffer;
|
||||
|
||||
/* Remote address as sockaddr */
|
||||
struct sockaddr_storage remote_saddr;
|
||||
socklen_t remote_saddr_len;
|
||||
|
||||
/* MUTEX LOCKED VARIABLES START */
|
||||
/* These variables only edited mutex locked */
|
||||
int running;
|
||||
int joined;
|
||||
|
||||
float volume;
|
||||
int volume_changed;
|
||||
unsigned char *metadata;
|
||||
int metadata_len;
|
||||
unsigned char *coverart;
|
||||
int coverart_len;
|
||||
char *dacp_id;
|
||||
char *active_remote_header;
|
||||
unsigned int progress_start;
|
||||
unsigned int progress_curr;
|
||||
unsigned int progress_end;
|
||||
int progress_changed;
|
||||
|
||||
int flush;
|
||||
thread_handle_t thread;
|
||||
mutex_handle_t run_mutex;
|
||||
/* MUTEX LOCKED VARIABLES END */
|
||||
|
||||
/* Remote control and timing ports */
|
||||
unsigned short control_rport;
|
||||
|
||||
/* Sockets for control and data */
|
||||
int csock, dsock;
|
||||
|
||||
/* Local control, timing and data ports */
|
||||
unsigned short control_lport;
|
||||
unsigned short data_lport;
|
||||
|
||||
/* Initialized after the first control packet */
|
||||
struct sockaddr_storage control_saddr;
|
||||
socklen_t control_saddr_len;
|
||||
unsigned short control_seqnum;
|
||||
};
|
||||
|
||||
static int
|
||||
raop_rtp_parse_remote(raop_rtp_t *raop_rtp, const unsigned char *remote, int remotelen)
|
||||
{
|
||||
char current[25];
|
||||
int family;
|
||||
int ret;
|
||||
assert(raop_rtp);
|
||||
if (remotelen == 4) {
|
||||
family = AF_INET;
|
||||
} else if (remotelen == 16) {
|
||||
family = AF_INET6;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
memset(current, 0, sizeof(current));
|
||||
sprintf(current, "%d.%d.%d.%d", remote[0], remote[1], remote[2], remote[3]);
|
||||
logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp parse remote ip = %s", current);
|
||||
ret = netutils_parse_address(family, current,
|
||||
&raop_rtp->remote_saddr,
|
||||
sizeof(raop_rtp->remote_saddr));
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
raop_rtp->remote_saddr_len = ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
raop_rtp_t *
|
||||
raop_rtp_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp, const unsigned char *remote, int remotelen,
|
||||
const unsigned char *aeskey, const unsigned char *aesiv, const unsigned char *ecdh_secret)
|
||||
{
|
||||
raop_rtp_t *raop_rtp;
|
||||
|
||||
assert(logger);
|
||||
assert(callbacks);
|
||||
|
||||
raop_rtp = calloc(1, sizeof(raop_rtp_t));
|
||||
if (!raop_rtp) {
|
||||
return NULL;
|
||||
}
|
||||
raop_rtp->logger = logger;
|
||||
raop_rtp->ntp = ntp;
|
||||
|
||||
raop_rtp->rtp_sync_offset = 0;
|
||||
raop_rtp->rtp_sync_scale = RAOP_RTP_SAMPLE_RATE;
|
||||
raop_rtp->sync_data_index = 0;
|
||||
for (int i = 0; i < RAOP_RTP_SYNC_DATA_COUNT; ++i) {
|
||||
raop_rtp->sync_data[i].ntp_time = 0;
|
||||
raop_rtp->sync_data[i].rtp_time = 0;
|
||||
}
|
||||
|
||||
memcpy(&raop_rtp->callbacks, callbacks, sizeof(raop_callbacks_t));
|
||||
raop_rtp->buffer = raop_buffer_init(logger, aeskey, aesiv, ecdh_secret);
|
||||
if (!raop_rtp->buffer) {
|
||||
free(raop_rtp);
|
||||
return NULL;
|
||||
}
|
||||
if (raop_rtp_parse_remote(raop_rtp, remote, remotelen) < 0) {
|
||||
free(raop_rtp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
raop_rtp->running = 0;
|
||||
raop_rtp->joined = 1;
|
||||
raop_rtp->flush = NO_FLUSH;
|
||||
|
||||
MUTEX_CREATE(raop_rtp->run_mutex);
|
||||
return raop_rtp;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
raop_rtp_destroy(raop_rtp_t *raop_rtp)
|
||||
{
|
||||
if (raop_rtp) {
|
||||
raop_rtp_stop(raop_rtp);
|
||||
MUTEX_DESTROY(raop_rtp->run_mutex);
|
||||
raop_buffer_destroy(raop_rtp->buffer);
|
||||
free(raop_rtp->metadata);
|
||||
free(raop_rtp->coverart);
|
||||
free(raop_rtp->dacp_id);
|
||||
free(raop_rtp->active_remote_header);
|
||||
free(raop_rtp);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
raop_rtp_resend_callback(void *opaque, unsigned short seqnum, unsigned short count)
|
||||
{
|
||||
raop_rtp_t *raop_rtp = opaque;
|
||||
unsigned char packet[8];
|
||||
unsigned short ourseqnum;
|
||||
struct sockaddr *addr;
|
||||
socklen_t addrlen;
|
||||
int ret;
|
||||
|
||||
addr = (struct sockaddr *)&raop_rtp->control_saddr;
|
||||
addrlen = raop_rtp->control_saddr_len;
|
||||
|
||||
logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp got resend request %d %d", seqnum, count);
|
||||
ourseqnum = raop_rtp->control_seqnum++;
|
||||
|
||||
/* Fill the request buffer */
|
||||
packet[0] = 0x80;
|
||||
packet[1] = 0x55|0x80;
|
||||
packet[2] = (ourseqnum >> 8);
|
||||
packet[3] = ourseqnum;
|
||||
packet[4] = (seqnum >> 8);
|
||||
packet[5] = seqnum;
|
||||
packet[6] = (count >> 8);
|
||||
packet[7] = count;
|
||||
|
||||
ret = sendto(raop_rtp->csock, (const char *)packet, sizeof(packet), 0, addr, addrlen);
|
||||
if (ret == -1) {
|
||||
logger_log(raop_rtp->logger, LOGGER_WARNING, "raop_rtp resend failed: %d", SOCKET_GET_ERROR());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
raop_rtp_init_sockets(raop_rtp_t *raop_rtp, int use_ipv6, int use_udp)
|
||||
{
|
||||
int csock = -1, dsock = -1;
|
||||
unsigned short cport = 0, dport = 0;
|
||||
|
||||
assert(raop_rtp);
|
||||
|
||||
csock = netutils_init_socket(&cport, use_ipv6, 1);
|
||||
dsock = netutils_init_socket(&dport, use_ipv6, 1);
|
||||
|
||||
if (csock == -1 || dsock == -1) {
|
||||
goto sockets_cleanup;
|
||||
}
|
||||
|
||||
/* Set socket descriptors */
|
||||
raop_rtp->csock = csock;
|
||||
raop_rtp->dsock = dsock;
|
||||
|
||||
/* Set port values */
|
||||
raop_rtp->control_lport = cport;
|
||||
raop_rtp->data_lport = dport;
|
||||
return 0;
|
||||
|
||||
sockets_cleanup:
|
||||
if (csock != -1) closesocket(csock);
|
||||
if (dsock != -1) closesocket(dsock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
raop_rtp_process_events(raop_rtp_t *raop_rtp, void *cb_data)
|
||||
{
|
||||
int flush;
|
||||
float volume;
|
||||
int volume_changed;
|
||||
unsigned char *metadata;
|
||||
int metadata_len;
|
||||
unsigned char *coverart;
|
||||
int coverart_len;
|
||||
char *dacp_id;
|
||||
char *active_remote_header;
|
||||
unsigned int progress_start;
|
||||
unsigned int progress_curr;
|
||||
unsigned int progress_end;
|
||||
int progress_changed;
|
||||
|
||||
assert(raop_rtp);
|
||||
|
||||
MUTEX_LOCK(raop_rtp->run_mutex);
|
||||
if (!raop_rtp->running) {
|
||||
MUTEX_UNLOCK(raop_rtp->run_mutex);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Read the volume level */
|
||||
volume = raop_rtp->volume;
|
||||
volume_changed = raop_rtp->volume_changed;
|
||||
raop_rtp->volume_changed = 0;
|
||||
|
||||
/* Read the flush value */
|
||||
flush = raop_rtp->flush;
|
||||
raop_rtp->flush = NO_FLUSH;
|
||||
|
||||
/* Read the metadata */
|
||||
metadata = raop_rtp->metadata;
|
||||
metadata_len = raop_rtp->metadata_len;
|
||||
raop_rtp->metadata = NULL;
|
||||
raop_rtp->metadata_len = 0;
|
||||
|
||||
/* Read the coverart */
|
||||
coverart = raop_rtp->coverart;
|
||||
coverart_len = raop_rtp->coverart_len;
|
||||
raop_rtp->coverart = NULL;
|
||||
raop_rtp->coverart_len = 0;
|
||||
|
||||
/* Read DACP remote control data */
|
||||
dacp_id = raop_rtp->dacp_id;
|
||||
active_remote_header = raop_rtp->active_remote_header;
|
||||
raop_rtp->dacp_id = NULL;
|
||||
raop_rtp->active_remote_header = NULL;
|
||||
|
||||
/* Read the progress values */
|
||||
progress_start = raop_rtp->progress_start;
|
||||
progress_curr = raop_rtp->progress_curr;
|
||||
progress_end = raop_rtp->progress_end;
|
||||
progress_changed = raop_rtp->progress_changed;
|
||||
raop_rtp->progress_changed = 0;
|
||||
|
||||
MUTEX_UNLOCK(raop_rtp->run_mutex);
|
||||
|
||||
/* Call set_volume callback if changed */
|
||||
if (volume_changed) {
|
||||
raop_buffer_flush(raop_rtp->buffer, flush);
|
||||
if (raop_rtp->callbacks.audio_set_volume) {
|
||||
raop_rtp->callbacks.audio_set_volume(raop_rtp->callbacks.cls, volume);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle flush if requested */
|
||||
if (flush != NO_FLUSH) {
|
||||
if (raop_rtp->callbacks.audio_flush) {
|
||||
raop_rtp->callbacks.audio_flush(raop_rtp->callbacks.cls);
|
||||
}
|
||||
}
|
||||
|
||||
if (metadata != NULL) {
|
||||
if (raop_rtp->callbacks.audio_set_metadata) {
|
||||
raop_rtp->callbacks.audio_set_metadata(raop_rtp->callbacks.cls, metadata, metadata_len);
|
||||
}
|
||||
free(metadata);
|
||||
metadata = NULL;
|
||||
}
|
||||
|
||||
if (coverart != NULL) {
|
||||
if (raop_rtp->callbacks.audio_set_coverart) {
|
||||
raop_rtp->callbacks.audio_set_coverart(raop_rtp->callbacks.cls, coverart, coverart_len);
|
||||
}
|
||||
free(coverart);
|
||||
coverart = NULL;
|
||||
}
|
||||
if (dacp_id && active_remote_header) {
|
||||
if (raop_rtp->callbacks.audio_remote_control_id) {
|
||||
raop_rtp->callbacks.audio_remote_control_id(raop_rtp->callbacks.cls, dacp_id, active_remote_header);
|
||||
}
|
||||
free(dacp_id);
|
||||
free(active_remote_header);
|
||||
dacp_id = NULL;
|
||||
active_remote_header = NULL;
|
||||
}
|
||||
|
||||
if (progress_changed) {
|
||||
if (raop_rtp->callbacks.audio_set_progress) {
|
||||
raop_rtp->callbacks.audio_set_progress(raop_rtp->callbacks.cls, progress_start, progress_curr, progress_end);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void raop_rtp_sync_clock(raop_rtp_t *raop_rtp, uint32_t rtp_time, uint64_t ntp_time) {
|
||||
raop_rtp->sync_data_index = (raop_rtp->sync_data_index + 1) % RAOP_RTP_SYNC_DATA_COUNT;
|
||||
raop_rtp->sync_data[raop_rtp->sync_data_index].rtp_time = rtp_time;
|
||||
raop_rtp->sync_data[raop_rtp->sync_data_index].ntp_time = ntp_time;
|
||||
|
||||
uint32_t valid_data_count = 0;
|
||||
valid_data_count = 0;
|
||||
int64_t total_offsets = 0;
|
||||
for (int i = 0; i < RAOP_RTP_SYNC_DATA_COUNT; ++i) {
|
||||
if (raop_rtp->sync_data[i].ntp_time == 0) continue;
|
||||
total_offsets += (int64_t) (((double) raop_rtp->sync_data[i].rtp_time) / raop_rtp->rtp_sync_scale) - raop_rtp->sync_data[i].ntp_time;
|
||||
valid_data_count++;
|
||||
}
|
||||
int64_t avg_offset = total_offsets / valid_data_count;
|
||||
int64_t correction = avg_offset - raop_rtp->rtp_sync_offset;
|
||||
raop_rtp->rtp_sync_offset = avg_offset;
|
||||
|
||||
logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp sync correction=%lld", correction);
|
||||
}
|
||||
|
||||
uint64_t raop_rtp_convert_rtp_time(raop_rtp_t *raop_rtp, uint32_t rtp_time) {
|
||||
return (uint64_t) (((double) rtp_time) / raop_rtp->rtp_sync_scale) - raop_rtp->rtp_sync_offset;
|
||||
}
|
||||
|
||||
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, NULL)) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set timeout value to 5ms */
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 5000;
|
||||
|
||||
/* Get the correct nfds value */
|
||||
nfds = raop_rtp->csock+1;
|
||||
if (raop_rtp->dsock >= nfds)
|
||||
nfds = raop_rtp->dsock+1;
|
||||
|
||||
/* Set rfds and call select */
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(raop_rtp->csock, &rfds);
|
||||
FD_SET(raop_rtp->dsock, &rfds);
|
||||
|
||||
ret = select(nfds, &rfds, NULL, NULL, &tv);
|
||||
if (ret == 0) {
|
||||
/* Timeout happened */
|
||||
continue;
|
||||
} else if (ret == -1) {
|
||||
logger_log(raop_rtp->logger, LOGGER_ERR, "raop_rtp error in select");
|
||||
break;
|
||||
}
|
||||
|
||||
if (FD_ISSET(raop_rtp->csock, &rfds)) {
|
||||
saddrlen = sizeof(saddr);
|
||||
packetlen = recvfrom(raop_rtp->csock, (char *)packet, sizeof(packet), 0,
|
||||
(struct sockaddr *)&saddr, &saddrlen);
|
||||
|
||||
memcpy(&raop_rtp->control_saddr, &saddr, saddrlen);
|
||||
raop_rtp->control_saddr_len = saddrlen;
|
||||
int type_c = packet[1] & ~0x80;
|
||||
logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp type_c 0x%02x, packetlen = %d", type_c, packetlen);
|
||||
if (type_c == 0x56) {
|
||||
/* Handle resent data packet */
|
||||
uint32_t rtp_timestamp = (packet[4 + 4] << 24) | (packet[4 + 5] << 16) | (packet[4 + 6] << 8) | packet[4 + 7];
|
||||
uint64_t ntp_timestamp = raop_rtp_convert_rtp_time(raop_rtp, rtp_timestamp);
|
||||
uint64_t ntp_now = raop_ntp_get_local_time(raop_rtp->ntp);
|
||||
logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp audio resent: ntp = %llu, now = %llu, latency=%lld, rtp=%u",
|
||||
ntp_timestamp, ntp_now, ((int64_t) ntp_now) - ((int64_t) ntp_timestamp), rtp_timestamp);
|
||||
int result = raop_buffer_enqueue(raop_rtp->buffer, packet + 4, packetlen - 4, ntp_timestamp, 1);
|
||||
assert(result >= 0);
|
||||
} else if (type_c == 0x54 && packetlen >= 20) {
|
||||
// The unit for the rtp clock is 1 / sample rate = 1 / 44100
|
||||
uint32_t sync_rtp = byteutils_get_int_be(packet, 4) - 11025;
|
||||
uint64_t sync_ntp_raw = byteutils_get_long_be(packet, 8);
|
||||
uint64_t sync_ntp_remote = raop_ntp_timestamp_to_micro_seconds(sync_ntp_raw, true);
|
||||
uint64_t sync_ntp_local = raop_ntp_convert_remote_time(raop_rtp->ntp, sync_ntp_remote);
|
||||
// It's not clear what the additional rtp timestamp indicates
|
||||
uint32_t next_rtp = byteutils_get_int_be(packet, 16);
|
||||
logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp sync: ntp=%llu, local ntp: %llu, rtp=%u, rtp_next=%u",
|
||||
sync_ntp_remote, sync_ntp_local, sync_rtp, next_rtp);
|
||||
raop_rtp_sync_clock(raop_rtp, sync_rtp, sync_ntp_local);
|
||||
} else {
|
||||
logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp unknown packet");
|
||||
}
|
||||
}
|
||||
|
||||
if (FD_ISSET(raop_rtp->dsock, &rfds)) {
|
||||
//logger_log(raop_rtp->logger, LOGGER_INFO, "Would have data packet in queue");
|
||||
// Receiving audio data here
|
||||
saddrlen = sizeof(saddr);
|
||||
packetlen = recvfrom(raop_rtp->dsock, (char *)packet, sizeof(packet), 0,
|
||||
(struct sockaddr *)&saddr, &saddrlen);
|
||||
// rtp payload type
|
||||
int type_d = packet[1] & ~0x80;
|
||||
//logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp_thread_udp type_d 0x%02x, packetlen = %d", type_d, packetlen);
|
||||
|
||||
// Len = 16 appears if there is no time
|
||||
if (packetlen >= 12) {
|
||||
int no_resend = (raop_rtp->control_rport == 0);// false
|
||||
|
||||
uint32_t rtp_timestamp = (packet[4] << 24) | (packet[5] << 16) | (packet[6] << 8) | packet[7];
|
||||
uint64_t ntp_timestamp = raop_rtp_convert_rtp_time(raop_rtp, rtp_timestamp);
|
||||
uint64_t ntp_now = raop_ntp_get_local_time(raop_rtp->ntp);
|
||||
logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp audio: ntp = %llu, now = %llu, latency=%lld, rtp=%u",
|
||||
ntp_timestamp, ntp_now, ((int64_t) ntp_now) - ((int64_t) ntp_timestamp), rtp_timestamp);
|
||||
|
||||
int result = raop_buffer_enqueue(raop_rtp->buffer, packet, packetlen, ntp_timestamp, 1);
|
||||
assert(result >= 0);
|
||||
|
||||
// Render continuous buffer entries
|
||||
void *payload = NULL;
|
||||
unsigned int payload_size;
|
||||
uint64_t timestamp;
|
||||
while ((payload = raop_buffer_dequeue(raop_rtp->buffer, &payload_size, ×tamp, no_resend))) {
|
||||
aac_decode_struct aac_data;
|
||||
aac_data.data_len = payload_size;
|
||||
aac_data.data = payload;
|
||||
aac_data.pts = timestamp;
|
||||
raop_rtp->callbacks.audio_process(raop_rtp->callbacks.cls, raop_rtp->ntp, &aac_data);
|
||||
free(payload);
|
||||
}
|
||||
|
||||
/* Handle possible resend requests */
|
||||
if (!no_resend) {
|
||||
raop_buffer_handle_resends(raop_rtp->buffer, raop_rtp_resend_callback, raop_rtp);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure running reflects the actual state
|
||||
MUTEX_LOCK(raop_rtp->run_mutex);
|
||||
raop_rtp->running = false;
|
||||
MUTEX_UNLOCK(raop_rtp->run_mutex);
|
||||
|
||||
logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp exiting thread");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Start rtp service, three udp ports
|
||||
void
|
||||
raop_rtp_start_audio(raop_rtp_t *raop_rtp, int use_udp, unsigned short control_rport,
|
||||
unsigned short *control_lport, unsigned short *data_lport)
|
||||
{
|
||||
logger_log(raop_rtp->logger, LOGGER_INFO, "raop_rtp starting audio");
|
||||
int use_ipv6 = 0;
|
||||
|
||||
assert(raop_rtp);
|
||||
|
||||
MUTEX_LOCK(raop_rtp->run_mutex);
|
||||
if (raop_rtp->running || !raop_rtp->joined) {
|
||||
MUTEX_UNLOCK(raop_rtp->run_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Initialize ports and sockets */
|
||||
raop_rtp->control_rport = control_rport;
|
||||
if (raop_rtp->remote_saddr.ss_family == AF_INET6) {
|
||||
use_ipv6 = 1;
|
||||
}
|
||||
use_ipv6 = 0;
|
||||
if (raop_rtp_init_sockets(raop_rtp, use_ipv6, use_udp) < 0) {
|
||||
logger_log(raop_rtp->logger, LOGGER_ERR, "raop_rtp initializing sockets failed");
|
||||
MUTEX_UNLOCK(raop_rtp->run_mutex);
|
||||
return;
|
||||
}
|
||||
if (control_lport) *control_lport = raop_rtp->control_lport;
|
||||
if (data_lport) *data_lport = raop_rtp->data_lport;
|
||||
/* Create the thread and initialize running values */
|
||||
raop_rtp->running = 1;
|
||||
raop_rtp->joined = 0;
|
||||
|
||||
THREAD_CREATE(raop_rtp->thread, raop_rtp_thread_udp, raop_rtp);
|
||||
MUTEX_UNLOCK(raop_rtp->run_mutex);
|
||||
}
|
||||
|
||||
void
|
||||
raop_rtp_set_volume(raop_rtp_t *raop_rtp, float volume)
|
||||
{
|
||||
assert(raop_rtp);
|
||||
|
||||
if (volume > 0.0f) {
|
||||
volume = 0.0f;
|
||||
} else if (volume < -144.0f) {
|
||||
volume = -144.0f;
|
||||
}
|
||||
|
||||
/* Set volume in thread instead */
|
||||
MUTEX_LOCK(raop_rtp->run_mutex);
|
||||
raop_rtp->volume = volume;
|
||||
raop_rtp->volume_changed = 1;
|
||||
MUTEX_UNLOCK(raop_rtp->run_mutex);
|
||||
}
|
||||
|
||||
void
|
||||
raop_rtp_set_metadata(raop_rtp_t *raop_rtp, const char *data, int datalen)
|
||||
{
|
||||
unsigned char *metadata;
|
||||
|
||||
assert(raop_rtp);
|
||||
|
||||
if (datalen <= 0) {
|
||||
return;
|
||||
}
|
||||
metadata = malloc(datalen);
|
||||
assert(metadata);
|
||||
memcpy(metadata, data, datalen);
|
||||
|
||||
/* Set metadata in thread instead */
|
||||
MUTEX_LOCK(raop_rtp->run_mutex);
|
||||
raop_rtp->metadata = metadata;
|
||||
raop_rtp->metadata_len = datalen;
|
||||
MUTEX_UNLOCK(raop_rtp->run_mutex);
|
||||
}
|
||||
|
||||
void
|
||||
raop_rtp_set_coverart(raop_rtp_t *raop_rtp, const char *data, int datalen)
|
||||
{
|
||||
unsigned char *coverart;
|
||||
|
||||
assert(raop_rtp);
|
||||
|
||||
if (datalen <= 0) {
|
||||
return;
|
||||
}
|
||||
coverart = malloc(datalen);
|
||||
assert(coverart);
|
||||
memcpy(coverart, data, datalen);
|
||||
|
||||
/* Set coverart in thread instead */
|
||||
MUTEX_LOCK(raop_rtp->run_mutex);
|
||||
raop_rtp->coverart = coverart;
|
||||
raop_rtp->coverart_len = datalen;
|
||||
MUTEX_UNLOCK(raop_rtp->run_mutex);
|
||||
}
|
||||
|
||||
void
|
||||
raop_rtp_remote_control_id(raop_rtp_t *raop_rtp, const char *dacp_id, const char *active_remote_header)
|
||||
{
|
||||
assert(raop_rtp);
|
||||
|
||||
if (!dacp_id || !active_remote_header) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set dacp stuff in thread instead */
|
||||
MUTEX_LOCK(raop_rtp->run_mutex);
|
||||
raop_rtp->dacp_id = strdup(dacp_id);
|
||||
raop_rtp->active_remote_header = strdup(active_remote_header);
|
||||
MUTEX_UNLOCK(raop_rtp->run_mutex);
|
||||
}
|
||||
|
||||
void
|
||||
raop_rtp_set_progress(raop_rtp_t *raop_rtp, unsigned int start, unsigned int curr, unsigned int end)
|
||||
{
|
||||
assert(raop_rtp);
|
||||
|
||||
/* Set progress in thread instead */
|
||||
MUTEX_LOCK(raop_rtp->run_mutex);
|
||||
raop_rtp->progress_start = start;
|
||||
raop_rtp->progress_curr = curr;
|
||||
raop_rtp->progress_end = end;
|
||||
raop_rtp->progress_changed = 1;
|
||||
MUTEX_UNLOCK(raop_rtp->run_mutex);
|
||||
}
|
||||
|
||||
void
|
||||
raop_rtp_flush(raop_rtp_t *raop_rtp, int next_seq)
|
||||
{
|
||||
assert(raop_rtp);
|
||||
|
||||
/* Call flush in thread instead */
|
||||
MUTEX_LOCK(raop_rtp->run_mutex);
|
||||
raop_rtp->flush = next_seq;
|
||||
MUTEX_UNLOCK(raop_rtp->run_mutex);
|
||||
}
|
||||
|
||||
void
|
||||
raop_rtp_stop(raop_rtp_t *raop_rtp)
|
||||
{
|
||||
assert(raop_rtp);
|
||||
|
||||
/* Check that we are running and thread is not
|
||||
* joined (should never be while still running) */
|
||||
MUTEX_LOCK(raop_rtp->run_mutex);
|
||||
if (!raop_rtp->running || raop_rtp->joined) {
|
||||
MUTEX_UNLOCK(raop_rtp->run_mutex);
|
||||
return;
|
||||
}
|
||||
raop_rtp->running = 0;
|
||||
MUTEX_UNLOCK(raop_rtp->run_mutex);
|
||||
|
||||
/* Join the thread */
|
||||
THREAD_JOIN(raop_rtp->thread);
|
||||
|
||||
if (raop_rtp->csock != -1) closesocket(raop_rtp->csock);
|
||||
if (raop_rtp->dsock != -1) closesocket(raop_rtp->dsock);
|
||||
|
||||
/* Flush buffer into initial state */
|
||||
raop_buffer_flush(raop_rtp->buffer, -1);
|
||||
|
||||
/* Mark thread as joined */
|
||||
MUTEX_LOCK(raop_rtp->run_mutex);
|
||||
raop_rtp->joined = 1;
|
||||
MUTEX_UNLOCK(raop_rtp->run_mutex);
|
||||
}
|
||||
|
||||
int
|
||||
raop_rtp_is_running(raop_rtp_t *raop_rtp)
|
||||
{
|
||||
assert(raop_rtp);
|
||||
MUTEX_LOCK(raop_rtp->run_mutex);
|
||||
int running = raop_rtp->running;
|
||||
MUTEX_UNLOCK(raop_rtp->run_mutex);
|
||||
return running;
|
||||
}
|
||||
|
||||
45
lib/raop_rtp.h
Executable file
45
lib/raop_rtp.h
Executable file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef RAOP_RTP_H
|
||||
#define RAOP_RTP_H
|
||||
|
||||
/* For raop_callbacks_t */
|
||||
#include "raop.h"
|
||||
#include "logger.h"
|
||||
#include "raop_ntp.h"
|
||||
|
||||
#define RAOP_AESIV_LEN 16
|
||||
#define RAOP_AESKEY_LEN 16
|
||||
#define RAOP_PACKET_LEN 32768
|
||||
|
||||
typedef struct raop_rtp_s raop_rtp_t;
|
||||
|
||||
raop_rtp_t *raop_rtp_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp, const unsigned char *remote, int remotelen,
|
||||
const unsigned char *aeskey, const unsigned char *aesiv, const unsigned char *ecdh_secret);
|
||||
|
||||
void raop_rtp_start_audio(raop_rtp_t *raop_rtp, int use_udp, unsigned short control_rport,
|
||||
unsigned short *control_lport, unsigned short *data_lport);
|
||||
|
||||
void raop_rtp_set_volume(raop_rtp_t *raop_rtp, float volume);
|
||||
void raop_rtp_set_metadata(raop_rtp_t *raop_rtp, const char *data, int datalen);
|
||||
void raop_rtp_set_coverart(raop_rtp_t *raop_rtp, const char *data, int datalen);
|
||||
void raop_rtp_remote_control_id(raop_rtp_t *raop_rtp, const char *dacp_id, const char *active_remote_header);
|
||||
void raop_rtp_set_progress(raop_rtp_t *raop_rtp, unsigned int start, unsigned int curr, unsigned int end);
|
||||
void raop_rtp_flush(raop_rtp_t *raop_rtp, int next_seq);
|
||||
void raop_rtp_stop(raop_rtp_t *raop_rtp);
|
||||
int raop_rtp_is_running(raop_rtp_t *raop_rtp);
|
||||
void raop_rtp_destroy(raop_rtp_t *raop_rtp);
|
||||
|
||||
#endif
|
||||
513
lib/raop_rtp_mirror.c
Executable file
513
lib/raop_rtp_mirror.c
Executable file
@@ -0,0 +1,513 @@
|
||||
/*
|
||||
* Copyright (c) 2019 dsafa22, All Rights Reserved.
|
||||
*
|
||||
* 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 "raop_rtp_mirror.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "raop.h"
|
||||
#include "netutils.h"
|
||||
#include "compat.h"
|
||||
#include "logger.h"
|
||||
#include "byteutils.h"
|
||||
#include "mirror_buffer.h"
|
||||
#include "stream.h"
|
||||
|
||||
|
||||
struct h264codec_s {
|
||||
unsigned char compatibility;
|
||||
short pps_size;
|
||||
short sps_size;
|
||||
unsigned char level;
|
||||
unsigned char number_of_pps;
|
||||
unsigned char* picture_parameter_set;
|
||||
unsigned char profile_high;
|
||||
unsigned char reserved_3_and_sps;
|
||||
unsigned char reserved_6_and_nal;
|
||||
unsigned char* sequence_parameter_set;
|
||||
unsigned char version;
|
||||
};
|
||||
|
||||
struct raop_rtp_mirror_s {
|
||||
logger_t *logger;
|
||||
raop_callbacks_t callbacks;
|
||||
raop_ntp_t *ntp;
|
||||
|
||||
/* Buffer to handle all resends */
|
||||
mirror_buffer_t *buffer;
|
||||
|
||||
/* Remote address as sockaddr */
|
||||
struct sockaddr_storage remote_saddr;
|
||||
socklen_t remote_saddr_len;
|
||||
|
||||
/* MUTEX LOCKED VARIABLES START */
|
||||
/* These variables only edited mutex locked */
|
||||
int running;
|
||||
int joined;
|
||||
|
||||
int flush;
|
||||
thread_handle_t thread_mirror;
|
||||
mutex_handle_t run_mutex;
|
||||
|
||||
/* MUTEX LOCKED VARIABLES END */
|
||||
int mirror_data_sock;
|
||||
|
||||
unsigned short mirror_data_lport;
|
||||
};
|
||||
|
||||
static int
|
||||
raop_rtp_parse_remote(raop_rtp_mirror_t *raop_rtp_mirror, const unsigned char *remote, int remotelen)
|
||||
{
|
||||
char current[25];
|
||||
int family;
|
||||
int ret;
|
||||
assert(raop_rtp_mirror);
|
||||
if (remotelen == 4) {
|
||||
family = AF_INET;
|
||||
} else if (remotelen == 16) {
|
||||
family = AF_INET6;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
memset(current, 0, sizeof(current));
|
||||
sprintf(current, "%d.%d.%d.%d", remote[0], remote[1], remote[2], remote[3]);
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror parse remote ip = %s", current);
|
||||
ret = netutils_parse_address(family, current,
|
||||
&raop_rtp_mirror->remote_saddr,
|
||||
sizeof(raop_rtp_mirror->remote_saddr));
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
raop_rtp_mirror->remote_saddr_len = ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define NO_FLUSH (-42)
|
||||
raop_rtp_mirror_t *raop_rtp_mirror_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp,
|
||||
const unsigned char *remote, int remotelen,
|
||||
const unsigned char *aeskey, const unsigned char *ecdh_secret)
|
||||
{
|
||||
raop_rtp_mirror_t *raop_rtp_mirror;
|
||||
|
||||
assert(logger);
|
||||
assert(callbacks);
|
||||
|
||||
raop_rtp_mirror = calloc(1, sizeof(raop_rtp_mirror_t));
|
||||
if (!raop_rtp_mirror) {
|
||||
return NULL;
|
||||
}
|
||||
raop_rtp_mirror->logger = logger;
|
||||
raop_rtp_mirror->ntp = ntp;
|
||||
|
||||
memcpy(&raop_rtp_mirror->callbacks, callbacks, sizeof(raop_callbacks_t));
|
||||
raop_rtp_mirror->buffer = mirror_buffer_init(logger, aeskey, ecdh_secret);
|
||||
if (!raop_rtp_mirror->buffer) {
|
||||
free(raop_rtp_mirror);
|
||||
return NULL;
|
||||
}
|
||||
if (raop_rtp_parse_remote(raop_rtp_mirror, remote, remotelen) < 0) {
|
||||
free(raop_rtp_mirror);
|
||||
return NULL;
|
||||
}
|
||||
raop_rtp_mirror->running = 0;
|
||||
raop_rtp_mirror->joined = 1;
|
||||
raop_rtp_mirror->flush = NO_FLUSH;
|
||||
|
||||
MUTEX_CREATE(raop_rtp_mirror->run_mutex);
|
||||
return raop_rtp_mirror;
|
||||
}
|
||||
|
||||
void
|
||||
raop_rtp_init_mirror_aes(raop_rtp_mirror_t *raop_rtp_mirror, uint64_t streamConnectionID)
|
||||
{
|
||||
mirror_buffer_init_aes(raop_rtp_mirror->buffer, streamConnectionID);
|
||||
}
|
||||
|
||||
//#define DUMP_H264
|
||||
|
||||
#define RAOP_PACKET_LEN 32768
|
||||
/**
|
||||
* Mirror
|
||||
*/
|
||||
static THREAD_RETVAL
|
||||
raop_rtp_mirror_thread(void *arg)
|
||||
{
|
||||
raop_rtp_mirror_t *raop_rtp_mirror = arg;
|
||||
assert(raop_rtp_mirror);
|
||||
|
||||
int stream_fd = -1;
|
||||
unsigned char packet[128];
|
||||
memset(packet, 0 , 128);
|
||||
unsigned char* payload = NULL;
|
||||
unsigned int readstart = 0;
|
||||
|
||||
#ifdef DUMP_H264
|
||||
// C decrypted
|
||||
FILE* file = fopen("/home/pi/Airplay.h264", "wb");
|
||||
// Encrypted source file
|
||||
FILE* file_source = fopen("/home/pi/Airplay.source", "wb");
|
||||
FILE* file_len = fopen("/home/pi/Airplay.len", "wb");
|
||||
#endif
|
||||
|
||||
while (1) {
|
||||
fd_set rfds;
|
||||
struct timeval tv;
|
||||
int nfds, ret;
|
||||
MUTEX_LOCK(raop_rtp_mirror->run_mutex);
|
||||
if (!raop_rtp_mirror->running) {
|
||||
MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
|
||||
break;
|
||||
}
|
||||
MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
|
||||
|
||||
/* Set timeout value to 5ms */
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 5000;
|
||||
|
||||
/* Get the correct nfds value and set rfds */
|
||||
FD_ZERO(&rfds);
|
||||
if (stream_fd == -1) {
|
||||
FD_SET(raop_rtp_mirror->mirror_data_sock, &rfds);
|
||||
nfds = raop_rtp_mirror->mirror_data_sock+1;
|
||||
} else {
|
||||
FD_SET(stream_fd, &rfds);
|
||||
nfds = stream_fd+1;
|
||||
}
|
||||
ret = select(nfds, &rfds, NULL, NULL, &tv);
|
||||
if (ret == 0) {
|
||||
/* Timeout happened */
|
||||
continue;
|
||||
} else if (ret == -1) {
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror error in select");
|
||||
break;
|
||||
}
|
||||
|
||||
if (stream_fd == -1 && FD_ISSET(raop_rtp_mirror->mirror_data_sock, &rfds)) {
|
||||
struct sockaddr_storage saddr;
|
||||
socklen_t saddrlen;
|
||||
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror accepting client");
|
||||
saddrlen = sizeof(saddr);
|
||||
stream_fd = accept(raop_rtp_mirror->mirror_data_sock, (struct sockaddr *)&saddr, &saddrlen);
|
||||
if (stream_fd == -1) {
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror error in accept %d %s", errno, strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
// We're calling recv for a certain amount of data, so we need a timeout
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 5000;
|
||||
if (setsockopt(stream_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror could not set stream socket timeout %d %s", errno, strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
readstart = 0;
|
||||
}
|
||||
|
||||
if (stream_fd != -1 && FD_ISSET(stream_fd, &rfds)) {
|
||||
|
||||
// The first 128 bytes are some kind of header for the payload that follows
|
||||
while (payload == NULL && readstart < 128) {
|
||||
ret = recv(stream_fd, packet + readstart, 128 - readstart, 0);
|
||||
if (ret <= 0) break;
|
||||
readstart = readstart + ret;
|
||||
}
|
||||
|
||||
if (payload == NULL && ret == 0) {
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror tcp socket closed");
|
||||
FD_CLR(stream_fd, &rfds);
|
||||
stream_fd = -1;
|
||||
continue;
|
||||
} else if (payload == NULL && ret == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) continue; // Timeouts can happen even if the connection is fine
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror error in header recv: %d", errno);
|
||||
break;
|
||||
}
|
||||
|
||||
int payload_size = byteutils_get_int(packet, 0);
|
||||
unsigned short payload_type = byteutils_get_short(packet, 4) & 0xff;
|
||||
unsigned short payload_option = byteutils_get_short(packet, 6);
|
||||
|
||||
if (payload == NULL) {
|
||||
payload = malloc(payload_size);
|
||||
readstart = 0;
|
||||
}
|
||||
|
||||
while (readstart < payload_size) {
|
||||
// Payload data
|
||||
ret = recv(stream_fd, payload + readstart, payload_size - readstart, 0);
|
||||
if (ret <= 0) break;
|
||||
readstart = readstart + ret;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror tcp socket closed");
|
||||
break;
|
||||
} else if (ret == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) continue; // Timeouts can happen even if the connection is fine
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror error in recv: %d", errno);
|
||||
break;
|
||||
}
|
||||
|
||||
if (payload_type == 0) {
|
||||
// Normal video data (VCL NAL)
|
||||
|
||||
// Conveniently, the video data is already stamped with the remote wall clock time,
|
||||
// so no additional clock syncing needed. The only thing odd here is that the video
|
||||
// ntp time stamps don't include the SECONDS_FROM_1900_TO_1970, so it's really just
|
||||
// counting micro seconds since last boot.
|
||||
uint64_t ntp_timestamp_raw = byteutils_get_long(packet, 8);
|
||||
uint64_t ntp_timestamp_remote = raop_ntp_timestamp_to_micro_seconds(ntp_timestamp_raw, false);
|
||||
uint64_t ntp_timestamp = raop_ntp_convert_remote_time(raop_rtp_mirror->ntp, ntp_timestamp_remote);
|
||||
|
||||
uint64_t ntp_now = raop_ntp_get_local_time(raop_rtp_mirror->ntp);
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror video ntp = %llu, now = %llu, latency = %lld",
|
||||
ntp_timestamp, ntp_now, ((int64_t) ntp_now) - ((int64_t) ntp_timestamp));
|
||||
|
||||
#ifdef DUMP_H264
|
||||
fwrite(payload, payload_size, 1, file_source);
|
||||
fwrite(&readstart, sizeof(readstart), 1, file_len);
|
||||
#endif
|
||||
|
||||
// Decrypt data
|
||||
unsigned char* payload_decrypted = malloc(payload_size);
|
||||
mirror_buffer_decrypt(raop_rtp_mirror->buffer, payload, payload_decrypted, payload_size);
|
||||
|
||||
int nalu_type = payload[4] & 0x1f;
|
||||
int nalu_size = 0;
|
||||
int nalus_count = 0;
|
||||
|
||||
// It seems the AirPlay protocol prepends NALs with their size, which we're replacing with the 4-byte
|
||||
// start code for the NAL Byte-Stream Format.
|
||||
while (nalu_size < payload_size) {
|
||||
int nc_len = (payload_decrypted[nalu_size + 0] << 24) | (payload_decrypted[nalu_size + 1] << 16) |
|
||||
(payload_decrypted[nalu_size + 2] << 8) | (payload_decrypted[nalu_size + 3]);
|
||||
assert(nc_len > 0);
|
||||
|
||||
payload_decrypted[nalu_size + 0] = 0;
|
||||
payload_decrypted[nalu_size + 1] = 0;
|
||||
payload_decrypted[nalu_size + 2] = 0;
|
||||
payload_decrypted[nalu_size + 3] = 1;
|
||||
nalu_size += nc_len + 4;
|
||||
nalus_count++;
|
||||
}
|
||||
|
||||
// logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "nalutype = %d", nalu_type);
|
||||
// logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "nalu_size = %d, payloadsize = %d nalus_count = %d",
|
||||
// nalu_size, payload_size, nalus_count);
|
||||
|
||||
#ifdef DUMP_H264
|
||||
fwrite(payload_decrypted, payload_size, 1, file);
|
||||
#endif
|
||||
|
||||
h264_decode_struct h264_data;
|
||||
h264_data.data_len = payload_size;
|
||||
h264_data.data = payload_decrypted;
|
||||
h264_data.frame_type = 1;
|
||||
h264_data.pts = ntp_timestamp;
|
||||
|
||||
raop_rtp_mirror->callbacks.video_process(raop_rtp_mirror->callbacks.cls, raop_rtp_mirror->ntp, &h264_data);
|
||||
free(payload_decrypted);
|
||||
|
||||
} else if ((payload_type & 255) == 1) {
|
||||
// The information in the payload contains an SPS and a PPS NAL
|
||||
|
||||
float width_source = byteutils_get_float(packet, 40);
|
||||
float height_source = byteutils_get_float(packet, 44);
|
||||
float width = byteutils_get_float(packet, 56);
|
||||
float height = byteutils_get_float(packet, 60);
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror width_source = %f height_source = %f width = %f height = %f",
|
||||
width_source, height_source, width, height);
|
||||
|
||||
// The sps_pps is not encrypted
|
||||
h264codec_t h264;
|
||||
h264.version = payload[0];
|
||||
h264.profile_high = payload[1];
|
||||
h264.compatibility = payload[2];
|
||||
h264.level = payload[3];
|
||||
h264.reserved_6_and_nal = payload[4];
|
||||
h264.reserved_3_and_sps = payload[5];
|
||||
h264.sps_size = (short) (((payload[6] & 255) << 8) + (payload[7] & 255));
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror sps size = %d", h264.sps_size);
|
||||
h264.sequence_parameter_set = malloc(h264.sps_size);
|
||||
memcpy(h264.sequence_parameter_set, payload + 8, h264.sps_size);
|
||||
h264.number_of_pps = payload[h264.sps_size + 8];
|
||||
h264.pps_size = (short) (((payload[h264.sps_size + 9] & 2040) + payload[h264.sps_size + 10]) & 255);
|
||||
h264.picture_parameter_set = malloc(h264.pps_size);
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror pps size = %d", h264.pps_size);
|
||||
memcpy(h264.picture_parameter_set, payload + h264.sps_size + 11, h264.pps_size);
|
||||
|
||||
if (h264.sps_size + h264.pps_size < 102400) {
|
||||
// Copy the sps and pps into a buffer to hand to the decoder
|
||||
int sps_pps_len = (h264.sps_size + h264.pps_size) + 8;
|
||||
unsigned char sps_pps[sps_pps_len];
|
||||
sps_pps[0] = 0;
|
||||
sps_pps[1] = 0;
|
||||
sps_pps[2] = 0;
|
||||
sps_pps[3] = 1;
|
||||
memcpy(sps_pps + 4, h264.sequence_parameter_set, h264.sps_size);
|
||||
sps_pps[h264.sps_size + 4] = 0;
|
||||
sps_pps[h264.sps_size + 5] = 0;
|
||||
sps_pps[h264.sps_size + 6] = 0;
|
||||
sps_pps[h264.sps_size + 7] = 1;
|
||||
memcpy(sps_pps + h264.sps_size + 8, h264.picture_parameter_set, h264.pps_size);
|
||||
|
||||
#ifdef DUMP_H264
|
||||
fwrite(sps_pps, sps_pps_len, 1, file);
|
||||
#endif
|
||||
|
||||
h264_decode_struct h264_data;
|
||||
h264_data.data_len = sps_pps_len;
|
||||
h264_data.data = sps_pps;
|
||||
h264_data.frame_type = 0;
|
||||
h264_data.pts = 0;
|
||||
raop_rtp_mirror->callbacks.video_process(raop_rtp_mirror->callbacks.cls, raop_rtp_mirror->ntp, &h264_data);
|
||||
}
|
||||
free(h264.picture_parameter_set);
|
||||
free(h264.sequence_parameter_set);
|
||||
}
|
||||
|
||||
free(payload);
|
||||
payload = NULL;
|
||||
memset(packet, 0, 128);
|
||||
readstart = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Close the stream file descriptor */
|
||||
if (stream_fd != -1) {
|
||||
closesocket(stream_fd);
|
||||
}
|
||||
|
||||
#ifdef DUMP_H264
|
||||
fclose(file);
|
||||
fclose(file_source);
|
||||
fclose(file_len);
|
||||
#endif
|
||||
|
||||
// Ensure running reflects the actual state
|
||||
MUTEX_LOCK(raop_rtp_mirror->run_mutex);
|
||||
raop_rtp_mirror->running = false;
|
||||
MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
|
||||
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror exiting TCP thread");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
raop_rtp_start_mirror(raop_rtp_mirror_t *raop_rtp_mirror, int use_udp, unsigned short *mirror_data_lport)
|
||||
{
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_INFO, "raop_rtp_mirror starting mirroring");
|
||||
int use_ipv6 = 0;
|
||||
|
||||
assert(raop_rtp_mirror);
|
||||
|
||||
MUTEX_LOCK(raop_rtp_mirror->run_mutex);
|
||||
if (raop_rtp_mirror->running || !raop_rtp_mirror->joined) {
|
||||
MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (raop_rtp_mirror->remote_saddr.ss_family == AF_INET6) {
|
||||
use_ipv6 = 1;
|
||||
}
|
||||
use_ipv6 = 0;
|
||||
if (raop_rtp_init_mirror_sockets(raop_rtp_mirror, use_ipv6) < 0) {
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror initializing sockets failed");
|
||||
MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
|
||||
return;
|
||||
}
|
||||
if (mirror_data_lport) *mirror_data_lport = raop_rtp_mirror->mirror_data_lport;
|
||||
|
||||
/* Create the thread and initialize running values */
|
||||
raop_rtp_mirror->running = 1;
|
||||
raop_rtp_mirror->joined = 0;
|
||||
|
||||
THREAD_CREATE(raop_rtp_mirror->thread_mirror, raop_rtp_mirror_thread, raop_rtp_mirror);
|
||||
MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
|
||||
}
|
||||
|
||||
void raop_rtp_mirror_stop(raop_rtp_mirror_t *raop_rtp_mirror) {
|
||||
assert(raop_rtp_mirror);
|
||||
|
||||
/* Check that we are running and thread is not
|
||||
* joined (should never be while still running) */
|
||||
MUTEX_LOCK(raop_rtp_mirror->run_mutex);
|
||||
if (!raop_rtp_mirror->running || raop_rtp_mirror->joined) {
|
||||
MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
|
||||
return;
|
||||
}
|
||||
raop_rtp_mirror->running = 0;
|
||||
MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
|
||||
|
||||
if (raop_rtp_mirror->mirror_data_sock != -1) {
|
||||
closesocket(raop_rtp_mirror->mirror_data_sock);
|
||||
raop_rtp_mirror->mirror_data_sock = -1;
|
||||
}
|
||||
|
||||
/* Join the thread */
|
||||
THREAD_JOIN(raop_rtp_mirror->thread_mirror);
|
||||
|
||||
/* Mark thread as joined */
|
||||
MUTEX_LOCK(raop_rtp_mirror->run_mutex);
|
||||
raop_rtp_mirror->joined = 1;
|
||||
MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
|
||||
}
|
||||
|
||||
void raop_rtp_mirror_destroy(raop_rtp_mirror_t *raop_rtp_mirror) {
|
||||
if (raop_rtp_mirror) {
|
||||
raop_rtp_mirror_stop(raop_rtp_mirror);
|
||||
MUTEX_DESTROY(raop_rtp_mirror->run_mutex);
|
||||
mirror_buffer_destroy(raop_rtp_mirror->buffer);
|
||||
free(raop_rtp_mirror);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
raop_rtp_init_mirror_sockets(raop_rtp_mirror_t *raop_rtp_mirror, int use_ipv6)
|
||||
{
|
||||
int dsock = -1;
|
||||
unsigned short dport = 0;
|
||||
|
||||
assert(raop_rtp_mirror);
|
||||
|
||||
dsock = netutils_init_socket(&dport, use_ipv6, 0);
|
||||
if (dsock == -1) {
|
||||
goto sockets_cleanup;
|
||||
}
|
||||
|
||||
/* Listen to the data socket if using TCP */
|
||||
if (listen(dsock, 1) < 0) {
|
||||
goto sockets_cleanup;
|
||||
}
|
||||
|
||||
/* Set socket descriptors */
|
||||
raop_rtp_mirror->mirror_data_sock = dsock;
|
||||
|
||||
/* Set port values */
|
||||
raop_rtp_mirror->mirror_data_lport = dport;
|
||||
return 0;
|
||||
|
||||
sockets_cleanup:
|
||||
if (dsock != -1) closesocket(dsock);
|
||||
return -1;
|
||||
}
|
||||
36
lib/raop_rtp_mirror.h
Executable file
36
lib/raop_rtp_mirror.h
Executable file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2019 dsafa22, All Rights Reserved.
|
||||
*
|
||||
* 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 RAOP_RTP_MIRROR_H
|
||||
#define RAOP_RTP_MIRROR_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "raop.h"
|
||||
#include "logger.h"
|
||||
#include "raop_ntp.h"
|
||||
|
||||
typedef struct raop_rtp_mirror_s raop_rtp_mirror_t;
|
||||
typedef struct h264codec_s h264codec_t;
|
||||
|
||||
raop_rtp_mirror_t *raop_rtp_mirror_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp,
|
||||
const unsigned char *remote, int remotelen,
|
||||
const unsigned char *aeskey, const unsigned char *ecdh_secret);
|
||||
void raop_rtp_init_mirror_aes(raop_rtp_mirror_t *raop_rtp_mirror, uint64_t streamConnectionID);
|
||||
void raop_rtp_start_mirror(raop_rtp_mirror_t *raop_rtp_mirror, int use_udp, unsigned short *mirror_data_lport);
|
||||
|
||||
static int raop_rtp_init_mirror_sockets(raop_rtp_mirror_t *raop_rtp_mirror, int use_ipv6);
|
||||
|
||||
void raop_rtp_mirror_stop(raop_rtp_mirror_t *raop_rtp_mirror);
|
||||
void raop_rtp_mirror_destroy(raop_rtp_mirror_t *raop_rtp_mirror);
|
||||
#endif //RAOP_RTP_MIRROR_H
|
||||
49
lib/sockets.h
Executable file
49
lib/sockets.h
Executable file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef SOCKETS_H
|
||||
#define SOCKETS_H
|
||||
|
||||
#if defined(WIN32)
|
||||
typedef int socklen_t;
|
||||
|
||||
#ifndef SHUT_RD
|
||||
# define SHUT_RD SD_RECEIVE
|
||||
#endif
|
||||
#ifndef SHUT_WR
|
||||
# define SHUT_WR SD_SEND
|
||||
#endif
|
||||
#ifndef SHUT_RDWR
|
||||
# define SHUT_RDWR SD_BOTH
|
||||
#endif
|
||||
|
||||
#define SOCKET_GET_ERROR() WSAGetLastError()
|
||||
#define SOCKET_SET_ERROR(value) WSASetLastError(value)
|
||||
#define SOCKET_ERRORNAME(name) WSA##name
|
||||
|
||||
#define WSAEAGAIN WSAEWOULDBLOCK
|
||||
#define WSAENOMEM WSA_NOT_ENOUGH_MEMORY
|
||||
|
||||
#else
|
||||
|
||||
#define closesocket close
|
||||
#define ioctlsocket ioctl
|
||||
|
||||
#define SOCKET_GET_ERROR() (errno)
|
||||
#define SOCKET_SET_ERROR(value) (errno = (value))
|
||||
#define SOCKET_ERRORNAME(name) name
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
36
lib/stream.h
Executable file
36
lib/stream.h
Executable file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2019 dsafa22, All Rights Reserved.
|
||||
*
|
||||
* 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 AIRPLAYSERVER_STREAM_H
|
||||
#define AIRPLAYSERVER_STREAM_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
int n_gop_index;
|
||||
int frame_type;
|
||||
int n_frame_poc;
|
||||
unsigned char *data;
|
||||
int data_len;
|
||||
unsigned int n_time_stamp;
|
||||
uint64_t pts;
|
||||
} h264_decode_struct;
|
||||
|
||||
typedef struct {
|
||||
unsigned char *data;
|
||||
int data_len;
|
||||
uint64_t pts;
|
||||
} aac_decode_struct;
|
||||
|
||||
#endif //AIRPLAYSERVER_STREAM_H
|
||||
66
lib/threads.h
Executable file
66
lib/threads.h
Executable file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef THREADS_H
|
||||
#define THREADS_H
|
||||
|
||||
#if defined(WIN32)
|
||||
#include <windows.h>
|
||||
|
||||
#define sleepms(x) Sleep(x)
|
||||
|
||||
typedef HANDLE thread_handle_t;
|
||||
|
||||
#define THREAD_RETVAL DWORD WINAPI
|
||||
#define THREAD_CREATE(handle, func, arg) \
|
||||
handle = CreateThread(NULL, 0, func, arg, 0, NULL)
|
||||
#define THREAD_JOIN(handle) do { WaitForSingleObject(handle, INFINITE); CloseHandle(handle); } while(0)
|
||||
|
||||
typedef HANDLE mutex_handle_t;
|
||||
|
||||
#define MUTEX_CREATE(handle) handle = CreateMutex(NULL, FALSE, NULL)
|
||||
#define MUTEX_LOCK(handle) WaitForSingleObject(handle, INFINITE)
|
||||
#define MUTEX_UNLOCK(handle) ReleaseMutex(handle)
|
||||
#define MUTEX_DESTROY(handle) CloseHandle(handle)
|
||||
|
||||
#else /* Use pthread library */
|
||||
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define sleepms(x) usleep((x)*1000)
|
||||
|
||||
typedef pthread_t thread_handle_t;
|
||||
|
||||
#define THREAD_RETVAL void *
|
||||
#define THREAD_CREATE(handle, func, arg) \
|
||||
if (pthread_create(&(handle), NULL, func, arg)) handle = 0
|
||||
#define THREAD_JOIN(handle) pthread_join(handle, NULL)
|
||||
|
||||
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_SIGNAL(handle) pthread_cond_signal(&(handle))
|
||||
#define COND_DESTROY(handle) pthread_cond_destroy(&(handle))
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* THREADS_H */
|
||||
181
lib/utils.c
Normal file
181
lib/utils.c
Normal file
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
char *
|
||||
utils_strsep(char **stringp, const char *delim)
|
||||
{
|
||||
char *original;
|
||||
char *strptr;
|
||||
|
||||
if (*stringp == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
original = *stringp;
|
||||
strptr = strstr(*stringp, delim);
|
||||
if (strptr == NULL) {
|
||||
*stringp = NULL;
|
||||
return original;
|
||||
}
|
||||
*strptr = '\0';
|
||||
*stringp = strptr+strlen(delim);
|
||||
return original;
|
||||
}
|
||||
|
||||
int
|
||||
utils_read_file(char **dst, const char *filename)
|
||||
{
|
||||
FILE *stream;
|
||||
int filesize;
|
||||
char *buffer;
|
||||
int read_bytes;
|
||||
|
||||
/* Open stream for reading */
|
||||
stream = fopen(filename, "rb");
|
||||
if (!stream) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Find out file size */
|
||||
fseek(stream, 0, SEEK_END);
|
||||
filesize = ftell(stream);
|
||||
fseek(stream, 0, SEEK_SET);
|
||||
|
||||
/* Allocate one extra byte for zero */
|
||||
buffer = malloc(filesize+1);
|
||||
if (!buffer) {
|
||||
fclose(stream);
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Read data in a loop to buffer */
|
||||
read_bytes = 0;
|
||||
do {
|
||||
int ret = fread(buffer+read_bytes, 1,
|
||||
filesize-read_bytes, stream);
|
||||
if (ret == 0) {
|
||||
break;
|
||||
}
|
||||
read_bytes += ret;
|
||||
} while (read_bytes < filesize);
|
||||
|
||||
/* Add final null byte and close stream */
|
||||
buffer[read_bytes] = '\0';
|
||||
fclose(stream);
|
||||
|
||||
/* If read didn't finish, return error */
|
||||
if (read_bytes != filesize) {
|
||||
free(buffer);
|
||||
return -3;
|
||||
}
|
||||
|
||||
/* Return buffer */
|
||||
*dst = buffer;
|
||||
return filesize;
|
||||
}
|
||||
|
||||
int
|
||||
utils_hwaddr_raop(char *str, int strlen, const char *hwaddr, int hwaddrlen)
|
||||
{
|
||||
int i,j;
|
||||
|
||||
/* Check that our string is long enough */
|
||||
if (strlen == 0 || strlen < 2*hwaddrlen+1)
|
||||
return -1;
|
||||
|
||||
/* Convert hardware address to hex string */
|
||||
for (i=0,j=0; i<hwaddrlen; i++) {
|
||||
int hi = (hwaddr[i]>>4) & 0x0f;
|
||||
int lo = hwaddr[i] & 0x0f;
|
||||
|
||||
if (hi < 10) str[j++] = '0' + hi;
|
||||
else str[j++] = 'A' + hi-10;
|
||||
if (lo < 10) str[j++] = '0' + lo;
|
||||
else str[j++] = 'A' + lo-10;
|
||||
}
|
||||
|
||||
/* Add string terminator */
|
||||
str[j++] = '\0';
|
||||
return j;
|
||||
}
|
||||
|
||||
int
|
||||
utils_hwaddr_airplay(char *str, int strlen, const char *hwaddr, int hwaddrlen)
|
||||
{
|
||||
int i,j;
|
||||
|
||||
/* Check that our string is long enough */
|
||||
if (strlen == 0 || strlen < 2*hwaddrlen+hwaddrlen)
|
||||
return -1;
|
||||
|
||||
/* Convert hardware address to hex string */
|
||||
for (i=0,j=0; i<hwaddrlen; i++) {
|
||||
int hi = (hwaddr[i]>>4) & 0x0f;
|
||||
int lo = hwaddr[i] & 0x0f;
|
||||
|
||||
if (hi < 10) str[j++] = '0' + hi;
|
||||
else str[j++] = 'a' + hi-10;
|
||||
if (lo < 10) str[j++] = '0' + lo;
|
||||
else str[j++] = 'a' + lo-10;
|
||||
|
||||
str[j++] = ':';
|
||||
}
|
||||
|
||||
/* Add string terminator */
|
||||
if (j != 0) j--;
|
||||
str[j++] = '\0';
|
||||
return j;
|
||||
}
|
||||
|
||||
char *utils_parse_hex(const char *str, int str_len, int *data_len) {
|
||||
assert(str_len % 2 == 0);
|
||||
|
||||
char *data = malloc(str_len / 2);
|
||||
|
||||
for (int i = 0; i < (str_len / 2); i++) {
|
||||
char c_1 = str[i * 2];
|
||||
if (c_1 >= 97 && c_1 <= 102) {
|
||||
c_1 -= (97 - 10);
|
||||
} else if (c_1 >= 65 && c_1 <= 70) {
|
||||
c_1 -= (65 - 10);
|
||||
} else if (c_1 >= 48 && c_1 <= 57) {
|
||||
c_1 -= 48;
|
||||
} else {
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char c_2 = str[(i * 2) + 1];
|
||||
if (c_2 >= 97 && c_2 <= 102) {
|
||||
c_2 -= (97 - 10);
|
||||
} else if (c_2 >= 65 && c_2 <= 70) {
|
||||
c_2 -= (65 - 10);
|
||||
} else if (c_2 >= 48 && c_2 <= 57) {
|
||||
c_2 -= 48;
|
||||
} else {
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data[i] = (c_1 << 4) | c_2;
|
||||
}
|
||||
|
||||
*data_len = (str_len / 2);
|
||||
return data;
|
||||
}
|
||||
Reference in New Issue
Block a user