Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merged in feature-angel-init

  • Loading branch information...
commit f7452af7f6ca9d90549b29953f7cdd8d9b6db819 2 parents 937c993 + 86cfb7b
Caleb James DeLisle authored
Showing with 3,293 additions and 632 deletions.
  1. +1 −0  .gitignore
  2. +9 −18 CMakeLists.txt
  3. +76 −57 admin/Admin.c
  4. +17 −8 admin/Admin.h
  5. +1 −1  admin/AdminClient.h
  6. +293 −0 admin/AdminLog.c
  7. +24 −0 admin/AdminLog.h
  8. +1 −1  admin/Admin_W32.c
  9. +13 −10 admin/CMakeLists.txt
  10. +78 −11 admin/Configurator.c
  11. +1 −1  admin/Configurator.h
  12. +370 −0 admin/angel/Angel.c
  13. +33 −0 admin/angel/Angel.h
  14. +105 −0 admin/angel/AngelChan.h
  15. +316 −0 admin/angel/AngelInit.c
  16. +36 −0 admin/angel/AngelInit.h
  17. +82 −0 admin/angel/CMakeLists.txt
  18. +32 −0 admin/angel/Cjdns.c
  19. +313 −0 admin/angel/Core.c
  20. +35 −0 admin/angel/Core.h
  21. +87 −0 admin/angel/Core_admin.c
  22. +33 −0 admin/angel/Core_admin.h
  23. +78 −0 admin/angel/Waiter.c
  24. +44 −0 admin/angel/Waiter.h
  25. +153 −280 cjdroute.c → admin/angel/cjdroute2.c
  26. +0 −10 admin/test/Admin_test.c
  27. +1 −1  admin/test/CMakeLists.txt
  28. +11 −5 admin/testframework/AdminTestFramework.c
  29. +1 −1  admin/testframework/AdminTestFramework.h
  30. +1 −1  admin/testframework/CMakeLists.txt
  31. +10 −3 benc/String.c
  32. +6 −0 benc/String.h
  33. +9 −4 cmake/modules/FindNACL.cmake
  34. +4 −1 contrib/python/bencode.py
  35. +22 −0 contrib/python/cjdns.py
  36. +72 −0 contrib/python/cjdnslog
  37. +3 −0  crypto/CMakeLists.txt
  38. +1 −1  crypto/CryptoAuth.c
  39. +1 −1  crypto/CryptoAuth.h
  40. +1 −1  crypto/CryptoAuth_benchmark.h
  41. +1 −1  crypto/CryptoAuth_pvt.h
  42. +2 −2 crypto/test/CryptoAuth_test.c
  43. +6 −5 crypto/test/CryptoAuth_unit_test.c
  44. +1 −1  dht/dhtcore/LinkStateNodeCollector.h
  45. +1 −1  dht/dhtcore/NodeCollector.h
  46. +1 −1  dht/dhtcore/NodeStore.c
  47. +1 −1  dht/dhtcore/NodeStore.h
  48. +1 −1  dht/dhtcore/NodeStore_pvt.h
  49. +1 −1  dht/dhtcore/RouterModule.c
  50. +1 −1  dht/dhtcore/RouterModule.h
  51. +1 −1  dht/dhtcore/SearchStore.c
  52. +1 −1  dht/dhtcore/SearchStore.h
  53. +2 −2 do
  54. +64 −0 exception/WriteErrorHandler.h
  55. +1 −13 interface/CMakeLists.txt
  56. +1 −1  interface/TUNConfigurator.h
  57. +1 −1  interface/UDPInterface.h
  58. +1 −1  interface/UDPInterface_admin.h
  59. +1 −1  interface/test/CMakeLists.txt
  60. +1 −1  interface/test/UDPInterface_test.c
  61. +1 −1  memory/MallocAllocator.c
  62. +1 −1  net/DefaultInterfaceController.h
  63. +2 −2 net/Ducttape.c
  64. +1 −1  net/Ducttape_pvt.h
  65. +1 −1  net/InterfaceController.h
  66. +1 −1  net/SwitchPinger.h
  67. +3 −2 net/test/DefaultInterfaceController_test.c
  68. +1 −1  switch/SwitchCore.c
  69. +1 −1  switch/SwitchCore.h
  70. +1 −1  test/CMakeLists.txt
  71. +3 −4 test/TestFramework.c
  72. +1 −1  util/Assert.h
  73. +1 −1  util/Bits.h
  74. +12 −2 util/CMakeLists.txt
  75. +0 −143 util/Log.h
  76. +1 −1  util/Pinger.h
  77. +38 −0 util/Process.h
  78. +66 −0 util/Process_FreeBSD.c
  79. +62 −0 util/Process_Illumos.c
  80. +62 −0 util/Process_Linux.c
  81. +64 −0 util/Process_OSX.c
  82. +11 −11 util/Security.c
  83. +1 −1  util/Security.h
  84. +1 −1  util/Security_W32.c
  85. +73 −0 util/Security_admin.c
  86. +24 −0 util/Security_admin.h
  87. +19 −0 util/log/CMakeLists.txt
  88. +38 −0 util/log/IndirectLog.c
  89. +29 −0 util/log/IndirectLog.h
  90. +160 −0 util/log/Log.h
  91. +67 −0 util/log/WriterLog.c
  92. +24 −0 util/log/WriterLog.h
  93. +2 −2 util/test/CMakeLists.txt
  94. +57 −0 util/test/Process_test.c
View
1  .gitignore
@@ -3,4 +3,5 @@ buildw32
*.pyc
.clang_complete
cjdroute
+cjdns
*~
View
27 CMakeLists.txt
@@ -41,11 +41,18 @@ endif()
if(CMAKE_SYSTEM_NAME STREQUAL SunOS)
set(ILLUMOS TRUE)
+ set(SYSTEM Illumos)
elseif(CMAKE_SYSTEM_NAME STREQUAL Linux)
set(LINUX TRUE)
+ set(SYSTEM Linux)
+elseif(CMAKE_SYSTEM_NAME STREQUAL Darwin)
+ set(APPLE TRUE)
+ set(SYSTEM OSX)
elseif(CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
set(FREEBSD TRUE)
+ set(SYSTEM FreeBSD)
endif()
+add_definitions(-D ${SYSTEM}=1}
if(CMAKE_C_COMPILER MATCHES "(.*)-gcc")
string(REGEX REPLACE "-gcc$" "" toolchain ${CMAKE_C_COMPILER})
@@ -194,6 +201,7 @@ endif()
# GIT_VERSION is used in dht/dhtcore/CMakeLists.txt
include_directories(${CMAKE_SOURCE_DIR})
+include_directories(${CMAKE_SOURCE_DIR}/src)
find_package(Libevent2 REQUIRED)
include_directories(${LIBEVENT2_INCLUDE_DIRS})
@@ -212,24 +220,7 @@ add_subdirectory(util)
add_subdirectory(switch)
add_subdirectory(net)
-add_executable(cjdroute cjdroute.c)
-target_link_libraries(cjdroute
- crypto
- crypto_benchmark
- interface
- switch
- dht
- dhtcore
- cjdbenc
- cjdbenc_JsonBencSerializer
- cjdmemory
- cjdadmin
- cjdnet
- ${LIBEVENT2_LIBRARIES}
- ${PLATFORM_LIBRARIES}
-)
-
-INSTALL(TARGETS cjdroute RUNTIME DESTINATION bin)
+#INSTALL(TARGETS cjdroute RUNTIME DESTINATION bin)
OPTION(NO_CODESYTLE "No codestyle checks")
View
133 admin/Admin.c
@@ -13,6 +13,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "admin/Admin.h"
+#include "admin/angel/AngelChan.h"
#include "benc/String.h"
#include "benc/Dict.h"
#include "benc/List.h"
@@ -30,7 +31,7 @@
#include "memory/BufferAllocator.h"
#include "util/Bits.h"
#include "util/Hex.h"
-#include "util/Log.h"
+#include "util/log/Log.h"
#include "util/Security.h"
#include "util/Time.h"
#include "util/Timeout.h"
@@ -48,15 +49,6 @@
#define EWOULDBLOCK WSAEWOULDBLOCK
#endif
-/* maximum message size for framing on the channels */
-#define MAX_MESSAGE_SIZE (1<<16)
-
-/* maximum message size for api requests (buffer size per active channel) */
-#define MAX_API_REQUEST_SIZE (1<<16)
-
-/* max connections to the admin tcp socket */
-#define MAX_CONNECTIONS 64
-
static String* TYPE = String_CONST_SO("type");
static String* REQUIRED = String_CONST_SO("required");
static String* STRING = String_CONST_SO("String");
@@ -188,16 +180,19 @@ struct Admin
struct Allocator* allocator;
struct sockaddr_storage address;
int addressLength;
- uint64_t pipeMagic;
+ uint64_t syncMagic;
String* password;
struct Log* logger;
unsigned int haveMessageHeaderLen;
struct Admin_MessageHeader messageHeader;
- struct Admin_Channel clientChannels[MAX_CONNECTIONS];
+ struct Admin_Channel clientChannels[AngelChan_MAX_CONNECTIONS];
- /** Becomes true after the admin process has sent it's first message. */
+ /**
+ * Becomes true after the admin process has sent it's first message.
+ * In cjdroute2 this is unused.
+ */
bool initialized;
};
@@ -213,11 +208,11 @@ static void adminClosePipes(struct Admin* admin)
/**
* find a channel by number; could allocate channels on the fly if needed
- * right now only 0..MAX_CONNECTIONS-1 numbers are valid and allocated in the Admin struct
+ * right now only 0..AngelChan_MAX_CONNECTIONS-1 numbers are valid and allocated in the Admin struct
*/
static struct Admin_Channel* adminChannelFindById(struct Admin* admin, uint32_t channelNum)
{
- if (channelNum < MAX_CONNECTIONS) {
+ if (channelNum < AngelChan_MAX_CONNECTIONS) {
return &admin->clientChannels[channelNum];
}
@@ -264,14 +259,14 @@ static void adminChannelSendData(struct Admin* admin,
uint32_t length)
{
/* if this changes, we need to fragment the messages
- * into MAX_MESSAGE_SIZE chunks
+ * into AngelChan_MAX_MESSAGE_SIZE chunks
*/
- Assert_compileTime(MAX_API_REQUEST_SIZE == MAX_MESSAGE_SIZE);
+ Assert_compileTime(AngelChan_MAX_API_REQUEST_SIZE == AngelChan_MAX_MESSAGE_SIZE);
- Assert_true(length <= MAX_MESSAGE_SIZE);
+ Assert_true(length <= AngelChan_MAX_MESSAGE_SIZE);
struct Admin_MessageHeader header = {
- .magic = admin->pipeMagic,
+ .magic = admin->syncMagic,
.length = length,
.channelNum = channelNum
};
@@ -287,10 +282,10 @@ static void adminChannelSendData(struct Admin* admin,
/**
* public function to send responses
*/
-void Admin_sendMessage(Dict* message, String* txid, struct Admin* admin)
+int Admin_sendMessage(Dict* message, String* txid, struct Admin* admin)
{
if (!admin) {
- return;
+ return 0;
}
Assert_true(txid);
@@ -298,13 +293,10 @@ void Admin_sendMessage(Dict* message, String* txid, struct Admin* admin)
struct Admin_Channel* channel = adminChannelFindByTxid(admin, txid, &channelNum);
if (!channel) {
- // txid too short, invalid channel number, closed channel or not matching serial
- Log_debug(admin->logger,
- "Dropped response because channel isn't open anymore.");
- return;
+ return Admin_sendMessage_CHANNEL_CLOSED;
}
- uint8_t buff[MAX_API_REQUEST_SIZE];
+ uint8_t buff[AngelChan_MAX_API_REQUEST_SIZE];
uint8_t allocBuff[256];
struct Allocator* allocator = BufferAllocator_new(allocBuff, 256);
@@ -318,10 +310,12 @@ void Admin_sendMessage(Dict* message, String* txid, struct Admin* admin)
Dict_putString(message, TXID, &userTxid, allocator);
}
- struct Writer* w = ArrayWriter_new(buff, sizeof(buff), allocator);
+ struct Writer* w = ArrayWriter_new(buff, AngelChan_MAX_API_REQUEST_SIZE, allocator);
StandardBencSerializer_get()->serializeDictionary(w, message);
adminChannelSendData(admin, channelNum, buff, w->bytesWritten(w));
+
+ return 0;
}
/**
@@ -531,6 +525,7 @@ static void handleRequestFromChild(struct Admin* admin,
return;
}
+// This is unused in cjdroute2, it only exists for legacy cjdroute.
static void inFromChildInitialize(struct Admin* admin)
{
uint8_t buffer[sizeof(struct sockaddr_storage) + sizeof(int) + 8];
@@ -673,13 +668,14 @@ static void inFromChildRead(struct Admin* admin, struct Admin_Channel* channel)
if (!channel->buffer) {
Assert_true(NULL == channel->allocator);
channel->allocator = admin->allocator->child(admin->allocator);
- channel->buffer = channel->allocator->malloc(MAX_API_REQUEST_SIZE, channel->allocator);
+ channel->buffer =
+ channel->allocator->malloc(AngelChan_MAX_API_REQUEST_SIZE, channel->allocator);
Assert_true(0 == channel->bufferLen);
}
- Assert_true(channel->bufferLen < MAX_API_REQUEST_SIZE);
+ Assert_true(channel->bufferLen < AngelChan_MAX_API_REQUEST_SIZE);
- int amount = inFromChildFillBuffer(admin, channel->buffer, MAX_API_REQUEST_SIZE,
+ int amount = inFromChildFillBuffer(admin, channel->buffer, AngelChan_MAX_API_REQUEST_SIZE,
&channel->bufferLen, admin->messageHeader.length);
if (amount < 0) {
return;
@@ -692,7 +688,7 @@ static void inFromChildRead(struct Admin* admin, struct Admin_Channel* channel)
inFromChildDecode(admin, channel);
- if (MAX_API_REQUEST_SIZE == channel->bufferLen) {
+ if (AngelChan_MAX_API_REQUEST_SIZE == channel->bufferLen) {
// couldn't decode the request, but the buffer is full
Log_error(admin->logger, "Request too large, closing channel [%u]",
admin->messageHeader.channelNum);
@@ -711,10 +707,10 @@ static void inFromChildRead(struct Admin* admin, struct Admin_Channel* channel)
static void inFromChildSkipMmsg(struct Admin* admin)
{
Assert_true(admin->messageHeader.length > 0);
- uint8_t buffer[MAX_MESSAGE_SIZE];
+ uint8_t buffer[AngelChan_MAX_MESSAGE_SIZE];
uint32_t have = 0;
- int amount = inFromChildFillBuffer(admin, buffer, MAX_MESSAGE_SIZE,
+ int amount = inFromChildFillBuffer(admin, buffer, AngelChan_MAX_MESSAGE_SIZE,
&have, admin->messageHeader.length);
if (amount < 0) {
return;
@@ -748,12 +744,12 @@ static void inFromChild(evutil_socket_t socket, short eventType, void* vcontext)
if (admin->haveMessageHeaderLen == Admin_MessageHeader_SIZE) {
newMessage = 1;
// got complete header
- if (admin->pipeMagic != admin->messageHeader.magic) {
+ if (admin->syncMagic != admin->messageHeader.magic) {
Log_error(admin->logger, "wrong magic from admin process");
adminClosePipes(admin);
return;
}
- if (admin->messageHeader.length > MAX_MESSAGE_SIZE) {
+ if (admin->messageHeader.length > AngelChan_MAX_MESSAGE_SIZE) {
Log_error(admin->logger, "message from admin process too large");
adminClosePipes(admin);
return;
@@ -811,11 +807,11 @@ struct ChildContext
{
unsigned int haveMessageHeaderLen, haveMessageLen;
struct Admin_MessageHeader messageHeader;
- uint8_t message[MAX_MESSAGE_SIZE];
+ uint8_t message[AngelChan_MAX_MESSAGE_SIZE];
- struct Connection connections[MAX_CONNECTIONS];
+ struct Connection connections[AngelChan_MAX_CONNECTIONS];
- uint64_t pipeMagic;
+ uint64_t syncMagic;
/** The event which listens for new connections. */
struct event* socketEvent;
@@ -836,7 +832,7 @@ struct ChildContext
static void sendParent(struct ChildContext* context, uint32_t channelNum, uint32_t len, void* data)
{
struct Admin_MessageHeader messageHeader = {
- .magic = context->pipeMagic,
+ .magic = context->syncMagic,
.length = len,
.channelNum = channelNum
};
@@ -888,11 +884,11 @@ static void incomingFromParent(evutil_socket_t socket, short eventType, void* vc
if (context->haveMessageHeaderLen == Admin_MessageHeader_SIZE) {
// got complete header, reset message
context->haveMessageLen = 0;
- if (context->pipeMagic != context->messageHeader.magic) {
+ if (context->syncMagic != context->messageHeader.magic) {
fprintf(stderr, "wrong magic on admin connection\n");
exit(0);
}
- if (context->messageHeader.length > MAX_MESSAGE_SIZE) {
+ if (context->messageHeader.length > AngelChan_MAX_MESSAGE_SIZE) {
fprintf(stderr, "message too large on admin connection\n");
exit(0);
}
@@ -924,9 +920,9 @@ static void incomingFromParent(evutil_socket_t socket, short eventType, void* vc
if (context->messageHeader.channelNum <= 0xffff) {
// message for admin connections
uint32_t connNumber = context->messageHeader.channelNum;
- if (connNumber >= MAX_CONNECTIONS) {
+ if (connNumber >= AngelChan_MAX_CONNECTIONS) {
fprintf(stderr, "got message for connection #%u, max connections is %d\n",
- connNumber, MAX_CONNECTIONS);
+ connNumber, AngelChan_MAX_CONNECTIONS);
return;
}
@@ -974,7 +970,7 @@ static void incomingFromClient(evutil_socket_t socket, short eventType, void* vc
{
struct Connection* conn = (struct Connection*) vconn;
struct ChildContext* context = conn->context;
- uint8_t buf[MAX_MESSAGE_SIZE];
+ uint8_t buf[AngelChan_MAX_MESSAGE_SIZE];
uint32_t connNumber = conn - context->connections;
errno = 0;
@@ -998,7 +994,7 @@ static void incomingFromClient(evutil_socket_t socket, short eventType, void* vc
static struct Connection* newConnection(struct ChildContext* context, evutil_socket_t fd)
{
struct Connection* conn = NULL;
- for (int i = 0; i < MAX_CONNECTIONS; i++) {
+ for (int i = 0; i < AngelChan_MAX_CONNECTIONS; i++) {
if (context->connections[i].read == NULL && context->connections[i].socket == -1) {
conn = &context->connections[i];
break;
@@ -1051,7 +1047,7 @@ static void child(struct sockaddr_storage* addr,
char* user,
struct ChildContext* context)
{
- for (int i = 0; i < MAX_CONNECTIONS; i++) {
+ for (int i = 0; i < AngelChan_MAX_CONNECTIONS; i++) {
context->connections[i].socket = -1;
}
@@ -1167,14 +1163,37 @@ void Admin_registerFunctionWithArgCount(char* name,
}
}
-struct Admin* Admin_new(struct sockaddr_storage* addr,
- int addrLen,
- String* password,
- char* user,
- struct event_base* eventBase,
- struct ExceptionHandler* eh,
+struct Admin* Admin_new(int fromAngelFd,
+ int toAngelFd,
+ struct Allocator* alloc,
struct Log* logger,
- struct Allocator* allocator)
+ struct event_base* eventBase,
+ String* password,
+ uint8_t syncMagic[8])
+{
+ struct Admin* admin = alloc->calloc(sizeof(struct Admin), 1, alloc);
+ admin->inFd = fromAngelFd;
+ admin->outFd = toAngelFd;
+ admin->allocator = alloc;
+ admin->logger = logger;
+ admin->functionCount = 0;
+ admin->eventBase = eventBase;
+ admin->password = password;
+ admin->pipeEv = event_new(eventBase, fromAngelFd, EV_READ | EV_PERSIST, inFromChild, admin);
+ admin->initialized = true;
+ event_add(admin->pipeEv, NULL);
+ Bits_memcpyConst(&admin->syncMagic, syncMagic, 8);
+ return admin;
+}
+
+struct Admin* Admin_newProc(struct sockaddr_storage* addr,
+ int addrLen,
+ String* password,
+ char* user,
+ struct event_base* eventBase,
+ struct ExceptionHandler* eh,
+ struct Log* logger,
+ struct Allocator* allocator)
{
if (!password) {
uint8_t buff[32];
@@ -1182,8 +1201,8 @@ struct Admin* Admin_new(struct sockaddr_storage* addr,
password = String_new((char*)buff, allocator);
}
- uint64_t pipeMagic;
- randombytes((uint8_t*)&pipeMagic, sizeof(pipeMagic));
+ uint64_t syncMagic;
+ randombytes((uint8_t*) &syncMagic, 8);
errno = 0;
int pipes[2][2];
@@ -1220,7 +1239,7 @@ struct Admin* Admin_new(struct sockaddr_storage* addr,
context.allocator = allocator;
event_reinit(eventBase);
context.eventBase = eventBase;
- context.pipeMagic = pipeMagic;
+ context.syncMagic = syncMagic;
child(addr, addrLen, user, &context);
fprintf(stderr, "Admin process exiting.");
exit(0);
@@ -1240,7 +1259,7 @@ struct Admin* Admin_new(struct sockaddr_storage* addr,
admin->addressLength = addrLen;
admin->pipeEv = event_new(eventBase, inFd, EV_READ | EV_PERSIST, inFromChild, admin);
event_add(admin->pipeEv, NULL);
- admin->pipeMagic = pipeMagic;
+ admin->syncMagic = syncMagic;
event_base_dispatch(eventBase);
View
25 admin/Admin.h
@@ -58,16 +58,25 @@ void Admin_registerFunctionWithArgCount(char* name,
Admin_registerFunctionWithArgCount( \
name, cb, ctx, needsAuth, args, (sizeof(args) / sizeof(struct Admin_FunctionArg)), admin)
-void Admin_sendMessage(Dict* message, String* txid, struct Admin* admin);
+#define Admin_sendMessage_CHANNEL_CLOSED -1
+int Admin_sendMessage(Dict* message, String* txid, struct Admin* admin);
-struct Admin* Admin_new(struct sockaddr_storage* addr,
- int addrLen,
- String* password,
- char* user,
- struct event_base* eventBase,
- struct ExceptionHandler* eh,
+struct Admin* Admin_new(int fromAngelFd,
+ int toAngelFd,
+ struct Allocator* alloc,
struct Log* logger,
- struct Allocator* allocator);
+ struct event_base* eventBase,
+ String* password,
+ uint8_t pipeMagic[8]);
+
+struct Admin* Admin_newProc(struct sockaddr_storage* addr,
+ int addrLen,
+ String* password,
+ char* user,
+ struct event_base* eventBase,
+ struct ExceptionHandler* eh,
+ struct Log* logger,
+ struct Allocator* allocator);
void Admin_getConnectInfo(struct sockaddr_storage** addrPtr,
int* addrLenPtr,
View
2  admin/AdminClient.h
@@ -18,7 +18,7 @@
#include "benc/String.h"
#include "benc/Dict.h"
#include "memory/Allocator.h"
-#include "util/Log.h"
+#include "util/log/Log.h"
#ifdef __FreeBSD__
#include <netinet/in.h>
#endif
View
293 admin/AdminLog.c
@@ -0,0 +1,293 @@
+/* vim: set expandtab ts=4 sw=4: */
+/*
+ * You may redistribute this program 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/>.
+ */
+#include "admin/Admin.h"
+#include "benc/Dict.h"
+#include "benc/String.h"
+#include "crypto/Crypto.h"
+#include "io/Writer.h"
+#include "memory/BufferAllocator.h"
+#include "util/log/Log.h"
+#include "util/Hex.h"
+
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <stdbool.h>
+
+#define MAX_SUBSCRIPTIONS 64
+#define FILE_NAME_COUNT 32
+
+struct Subscription
+{
+ /** The log level to match against, all higher levels will also be matched. */
+ enum Log_Level level;
+
+ /**
+ * true if the file name is the internal name
+ * which can be compared using a pointer equality check
+ */
+ bool internalName : 1;
+
+ /** The line number within the file or 0 to match all lines. */
+ int lineNum : 31;
+
+ /** The name of the file to match against or null to match any file. */
+ const char* file;
+
+ /** The transaction ID of the message which solicited this stream of logs. */
+ String* txid;
+
+ /** A hopefully unique (random) number identifying this stream. */
+ uint8_t streamId[8];
+
+ /** An allocator which will live during the lifecycle of the Subscription */
+ struct Allocator* alloc;
+};
+
+struct AdminLog
+{
+ struct Log pub;
+ struct Subscription subscriptions[MAX_SUBSCRIPTIONS];
+ uint32_t subscriptionCount;
+ const char* fileNames[FILE_NAME_COUNT];
+ struct Admin* admin;
+ struct Allocator* alloc;
+};
+
+static inline bool isMatch(struct Subscription* subscription,
+ struct AdminLog* logger,
+ enum Log_Level logLevel,
+ const char* file,
+ int line)
+{
+ if (subscription->file) {
+ if (subscription->internalName) {
+ if (file != subscription->file) {
+ return false;
+ }
+ } else if (strcmp(file, subscription->file)) {
+ return false;
+ } else {
+ // It's the same name but so we'll swap the name for the internal name and then
+ // it can be compared quickly with a pointer comparison.
+ subscription->file = file;
+ subscription->internalName = true;
+ for (int i = 0; i < FILE_NAME_COUNT; i++) {
+ if (logger->fileNames[i] == file) {
+ break;
+ }
+ if (logger->fileNames[i] == NULL) {
+ logger->fileNames[i] = file;
+ logger->fileNames[(i + 1) % FILE_NAME_COUNT] = NULL;
+ break;
+ }
+ }
+ }
+ }
+
+ if (logLevel < subscription->level) {
+ return false;
+ }
+ if (subscription->lineNum && line != subscription->lineNum) {
+ return false;
+ }
+ return true;
+}
+
+static Dict* makeLogMessage(struct Subscription* subscription,
+ struct AdminLog* logger,
+ enum Log_Level logLevel,
+ const char* file,
+ uint32_t line,
+ const char* format,
+ va_list vaArgs,
+ struct Allocator* alloc)
+{
+ time_t now;
+ time(&now);
+
+ Dict* out = Dict_new(alloc);
+ char* buff = alloc->malloc(20, alloc);
+ Hex_encode((uint8_t*)buff, 20, subscription->streamId, 8);
+ Dict_putString(out, String_new("streamId", alloc), String_new(buff, alloc), alloc);
+ Dict_putInt(out, String_new("time", alloc), now, alloc);
+ Dict_putString(out,
+ String_new("level", alloc),
+ String_new(Log_nameForLevel(logLevel), alloc),
+ alloc);
+ Dict_putString(out, String_new("file", alloc), String_new((char*)file, alloc), alloc);
+ Dict_putInt(out, String_new("line", alloc), line, alloc);
+ String* message = String_vprintf(alloc, format, vaArgs);
+
+ // Strip all of the annoying \n marks in the log entries.
+ if (message->len > 0 && message->bytes[message->len - 1] == '\n') {
+ message->len--;
+ }
+ Dict_putString(out, String_new("message", alloc), message, alloc);
+
+ return out;
+}
+
+static void removeSubscription(struct AdminLog* log, struct Subscription* sub)
+{
+ sub->alloc->free(sub->alloc);
+ log->subscriptionCount--;
+ if (log->subscriptionCount == 0) {
+ return;
+ }
+ Bits_memcpyConst(sub,
+ &log->subscriptions[log->subscriptionCount],
+ sizeof(struct Subscription));
+}
+
+static void doLog(struct Log* genericLog,
+ enum Log_Level logLevel,
+ const char* fullFilePath,
+ uint32_t line,
+ const char* format,
+ va_list args)
+{
+ const char* file = strrchr(fullFilePath, '/') + 1;
+ struct AdminLog* log = (struct AdminLog*) genericLog;
+ Dict* message = NULL;
+ #define ALLOC_BUFFER_SZ 4096
+ uint8_t allocBuffer[ALLOC_BUFFER_SZ];
+ for (int i = 0; i < (int)log->subscriptionCount; i++) {
+ if (isMatch(&log->subscriptions[i], log, logLevel, file, line)) {
+ if (!message) {
+ struct Allocator* alloc = BufferAllocator_new(allocBuffer, ALLOC_BUFFER_SZ);
+ message = makeLogMessage(&log->subscriptions[i],
+ log,
+ logLevel,
+ file,
+ line,
+ format,
+ args,
+ alloc);
+ }
+ int ret = Admin_sendMessage(message, log->subscriptions[i].txid, log->admin);
+ if (ret) {
+ removeSubscription(log, &log->subscriptions[i]);
+ }
+ }
+ }
+}
+
+static void subscribe(Dict* args, void* vcontext, String* txid)
+{
+ struct AdminLog* log = (struct AdminLog*) vcontext;
+ String* levelName = Dict_getString(args, String_CONST("level"));
+ enum Log_Level level = (levelName) ? Log_levelForName(levelName->bytes) : Log_Level_DEBUG;
+ int64_t* lineNumPtr = Dict_getInt(args, String_CONST("line"));
+ String* fileStr = Dict_getString(args, String_CONST("file"));
+ const char* file = (fileStr && fileStr->len > 0) ? fileStr->bytes : NULL;
+ char* error = "2+2=5";
+ if (level == Log_Level_INVALID) {
+ level = Log_Level_KEYS;
+ }
+ if (lineNumPtr && *lineNumPtr < 0) {
+ error = "Invalid line number, must be positive or 0 to signify any line is acceptable.";
+ } else if (log->subscriptionCount >= MAX_SUBSCRIPTIONS) {
+ error = "Max subscription count reached.";
+ } else {
+ struct Subscription* sub = &log->subscriptions[log->subscriptionCount];
+ sub->level = level;
+ sub->alloc = log->alloc->child(log->alloc);
+ if (file) {
+ int i;
+ for (i = 0; i < FILE_NAME_COUNT; i++) {
+ if (log->fileNames[i] && !strcmp(log->fileNames[i], file)) {
+ file = log->fileNames[i];
+ sub->internalName = true;
+ break;
+ }
+ }
+ if (i == FILE_NAME_COUNT) {
+ file = String_new(file, sub->alloc)->bytes;
+ sub->internalName = false;
+ }
+ }
+ sub->file = file;
+ sub->lineNum = (lineNumPtr) ? *lineNumPtr : 0;
+ sub->txid = String_clone(txid, sub->alloc);
+ randombytes((uint8_t*) sub->streamId, 8);
+ uint8_t streamIdHex[20];
+ Hex_encode(streamIdHex, 20, sub->streamId, 8);
+ Dict response = Dict_CONST(
+ String_CONST("error"), String_OBJ(String_CONST("none")), Dict_CONST(
+ String_CONST("streamId"), String_OBJ(String_CONST((char*)streamIdHex)), NULL
+ ));
+ Admin_sendMessage(&response, txid, log->admin);
+ log->subscriptionCount++;
+ return;
+ }
+
+ Dict response = Dict_CONST(
+ String_CONST("error"), String_OBJ(String_CONST(error)), NULL
+ );
+ Admin_sendMessage(&response, txid, log->admin);
+}
+
+static void unsubscribe(Dict* args, void* vcontext, String* txid)
+{
+ struct AdminLog* log = (struct AdminLog*) vcontext;
+ String* streamIdHex = Dict_getString(args, String_CONST("streamId"));
+ uint8_t streamId[8];
+ char* error = NULL;
+ if (streamIdHex->len != 16 || Hex_decode(streamId, 8, (uint8_t*)streamIdHex->bytes, 16) != 8) {
+ error = "Invalid streamId.";
+ } else {
+ error = "No such subscription.";
+ for (int i = 0; i < (int)log->subscriptionCount; i++) {
+ if (!memcmp(streamId, log->subscriptions[i].streamId, 8)) {
+ removeSubscription(log, &log->subscriptions[i]);
+ error = "none";
+ break;
+ }
+ }
+ }
+
+ Dict response = Dict_CONST(
+ String_CONST("error"), String_OBJ(String_CONST(error)), NULL
+ );
+ Admin_sendMessage(&response, txid, log->admin);
+}
+
+struct Log* AdminLog_registerNew(struct Admin* admin, struct Allocator* alloc)
+{
+ struct AdminLog* log = alloc->clone(sizeof(struct AdminLog), alloc, &(struct AdminLog) {
+ .pub = {
+ .callback = doLog
+ },
+ .admin = admin,
+ .alloc = alloc
+ });
+
+ struct Admin_FunctionArg subscribeArgs[] = {
+ { .name = "level", .required = 0, .type = "String" },
+ { .name = "line", .required = 0, .type = "Int" },
+ { .name = "file", .required = 0, .type = "String" }
+ };
+ Admin_registerFunction("AdminLog_subscribe", subscribe, log, true, subscribeArgs, admin);
+
+ struct Admin_FunctionArg unsubscribeArgs[] = {
+ { .name = "streamId", .required = 1, .type = "String" }
+ };
+ Admin_registerFunction("AdminLog_unsubscribe", unsubscribe, log, true, unsubscribeArgs, admin);
+
+ return &log->pub;
+}
View
24 admin/AdminLog.h
@@ -0,0 +1,24 @@
+/* vim: set expandtab ts=4 sw=4: */
+/*
+ * You may redistribute this program 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/>.
+ */
+#ifndef AdminLog_H
+#define AdminLog_H
+
+#include "admin/Admin.h"
+#include "memory/Allocator.h"
+#include "util/log/Log.h"
+
+struct Log* AdminLog_registerNew(struct Admin* admin, struct Allocator* alloc);
+
+#endif
View
2  admin/Admin_W32.c
@@ -29,7 +29,7 @@
#include "memory/BufferAllocator.h"
#include "util/Bits.h"
#include "util/Hex.h"
-#include "util/Log.h"
+#include "util/log/Log.h"
#include "util/Security.h"
#include "util/Time.h"
#include "util/Timeout.h"
View
23 admin/CMakeLists.txt
@@ -10,22 +10,25 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-if (WIN32)
- set(admin Admin_W32.c)
-else()
- set(admin Admin.c)
-endif()
+add_library(cjdns-admin
+ Admin.c
+ AuthorizedPasswords.c
+)
+target_link_libraries(cjdns-admin crypto cjdbenc_StandardBencSerializer)
+
+add_library(cjdns-admin-logger
+ AdminLog.c
+)
+target_link_libraries(cjdns-admin-logger cjdns-admin)
-add_library(cjdadmin
- ${admin}
+add_library(cjdns-admin-client
AdminClient.c
- AuthorizedPasswords.c
Configurator.c
)
-
-target_link_libraries(cjdadmin crypto cjdbenc_StandardBencSerializer)
+target_link_libraries(cjdns-admin-client crypto cjdbenc_StandardBencSerializer)
add_subdirectory(testframework)
+add_subdirectory(angel)
enable_testing()
add_subdirectory(test)
View
89 admin/Configurator.c
@@ -18,7 +18,7 @@
#include "benc/Dict.h"
#include "benc/Int.h"
#include "memory/Allocator.h"
-#include "util/Log.h"
+#include "util/log/Log.h"
#include <event2/event.h>
@@ -30,15 +30,29 @@ struct Context
struct AdminClient* client;
};
-static void showMsg(struct AdminClient_Result* res, struct Context* ctx)
+static void die(struct AdminClient_Result* res, struct Context* ctx, struct Allocator* alloc)
{
Log_keys(ctx->logger, "message bytes = [%s]", res->messageBytes);
#ifndef Log_KEYS
Log_critical(ctx->logger, "enable Log_LEVEL=KEYS to see message content.");
#endif
+
+ Dict d = NULL;
+ struct AdminClient_Result* exitRes =
+ AdminClient_rpcCall(String_CONST("Core_exit"), &d, ctx->client, alloc);
+
+ if (exitRes->err) {
+ Log_critical(ctx->logger, "Failed to stop the core.");
+ }
+ Log_critical(ctx->logger, "Aborting.");
+ exit(1);
}
-static void rpcCall(String* function, Dict* args, struct Context* ctx, struct Allocator* alloc)
+static void rpcCall0(String* function,
+ Dict* args,
+ struct Context* ctx,
+ struct Allocator* alloc,
+ bool exitIfError)
{
struct AdminClient_Result* res = AdminClient_rpcCall(function, args, ctx->client, alloc);
if (res->err) {
@@ -46,20 +60,27 @@ static void rpcCall(String* function, Dict* args, struct Context* ctx, struct Al
"Failed to make function call [%s], error: [%s]",
AdminClient_errorString(res->err),
function->bytes);
- showMsg(res, ctx);
- exit(-1);
+ die(res, ctx, alloc);
}
String* error = Dict_getString(res->responseDict, String_CONST("error"));
if (error && !String_equals(error, String_CONST("none"))) {
- Log_critical(ctx->logger,
- "Router responses with error: [%s]\nCalling function: [%s]",
- error->bytes,
- function->bytes);
- showMsg(res, ctx);
- exit(-1);
+ if (exitIfError) {
+ Log_critical(ctx->logger,
+ "Got error [%s] calling [%s]",
+ error->bytes,
+ function->bytes);
+ die(res, ctx, alloc);
+ }
+ Log_warn(ctx->logger, "Got error [%s] calling [%s], ignoring.",
+ error->bytes, function->bytes);
}
}
+static void rpcCall(String* function, Dict* args, struct Context* ctx, struct Allocator* alloc)
+{
+ rpcCall0(function, args, ctx, alloc, true);
+}
+
static void authorizedPasswords(List* list, struct Context* ctx)
{
uint32_t count = List_size(list);
@@ -134,6 +155,45 @@ static void udpInterface(Dict* config, struct Context* ctx)
}
}
+static void tunInterface(Dict* ifaceConf, struct Allocator* tempAlloc, struct Context* ctx)
+{
+ String* ifaceType = Dict_getString(ifaceConf, String_CONST("type"));
+ if (!String_equals(ifaceType, String_CONST("TUNInterface"))) {
+ return;
+ }
+
+ // Setup the interface.
+ String* device = Dict_getString(ifaceConf, String_CONST("tunDevice"));
+
+ Dict* args = Dict_new(tempAlloc);
+ if (device) {
+ Dict_putString(args, String_CONST("desiredTunName"), device, tempAlloc);
+ }
+ rpcCall(String_CONST("Core_initTunnel"), args, ctx, tempAlloc);
+}
+
+static void security(List* securityConf, struct Allocator* tempAlloc, struct Context* ctx)
+{
+ bool noFiles = false;
+ for (int i = 0; i < List_size(securityConf); i++) {
+ if (String_equals(String_CONST("nofiles"), List_getString(securityConf, i))) {
+ noFiles = true;
+ } else {
+ Dict* userDict = List_getDict(securityConf, i);
+ String* userName = Dict_getString(userDict, String_CONST("setuser"));
+ if (userName) {
+ Dict d = Dict_CONST(String_CONST("user"), String_OBJ(userName), NULL);
+ // If this call returns an error, it is ok.
+ rpcCall0(String_CONST("Security_setUser"), &d, ctx, tempAlloc, false);
+ }
+ }
+ }
+ if (noFiles) {
+ Dict d = NULL;
+ rpcCall(String_CONST("Security_noFiles"), &d, ctx, tempAlloc);
+ }
+}
+
void Configurator_config(Dict* config,
struct sockaddr_storage* addr,
int addrLen,
@@ -156,5 +216,12 @@ void Configurator_config(Dict* config,
Dict* ifaces = Dict_getDict(config, String_CONST("interfaces"));
udpInterface(ifaces, &ctx);
+ Dict* routerConf = Dict_getDict(config, String_CONST("router"));
+ Dict* iface = Dict_getDict(routerConf, String_CONST("interface"));
+ tunInterface(iface, tempAlloc, &ctx);
+
+ List* securityList = Dict_getList(config, String_CONST("security"));
+ security(securityList, tempAlloc, &ctx);
+
tempAlloc->free(tempAlloc);
}
View
2  admin/Configurator.h
@@ -18,7 +18,7 @@
#include "benc/String.h"
#include "benc/Dict.h"
#include "memory/Allocator.h"
-#include "util/Log.h"
+#include "util/log/Log.h"
#include <event2/event.h>
View
370 admin/angel/Angel.c
@@ -0,0 +1,370 @@
+/* vim: set expandtab ts=4 sw=4: */
+/*
+ * You may redistribute this program 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/>.
+ */
+#include "admin/angel/Angel.h"
+#include "admin/angel/AngelChan.h"
+#include "benc/String.h"
+#include "memory/Allocator.h"
+#include "util/Bits.h"
+#include "util/log/Log.h"
+#include "util/Time.h"
+#include "util/Timeout.h"
+
+#include <event2/event.h>
+#include <errno.h>
+#include <unistd.h>
+
+struct AngelContext;
+
+struct Connection {
+ struct event* read; /** NULL: socket closed */
+ evutil_socket_t socket; /** -1: channel (to core) closed */
+ struct AngelContext* context;
+};
+
+struct AngelContext
+{
+ unsigned int haveMessageHeaderLen, haveMessageLen;
+ struct AngelChan_MessageHeader messageHeader;
+ uint8_t message[AngelChan_MAX_MESSAGE_SIZE];
+
+ struct Connection connections[AngelChan_MAX_CONNECTIONS];
+
+ /**
+ * Magic bytes which are sent at the beginning of
+ * each frame to make sure the connection is still synchronized.
+ */
+ uint64_t syncMagic;
+
+ uint64_t timeOfLastMessageFromCore;
+
+ /** The event which listens for new connections. */
+ struct event* socketEvent;
+
+ /** For talking with the core process. */
+ struct event* fromCore;
+ int inFd;
+ int outFd;
+
+ struct event_base* eventBase;
+ struct Allocator* allocator;
+ struct Log* logger;
+};
+
+static void sendToCore(struct AngelContext* context, uint32_t channelNum, uint32_t len, void* data);
+
+/**
+ * @param fileDescriptor the file to read from.
+ * @param outputBuffer the buffer to write to.
+ * @param amountGotten the amount of data already read (this will be modified).
+ * @param totalExpected the amount to expect when reading.
+ */
+static void getDataFromPipe(int fileDescriptor,
+ uint8_t* outputBuffer,
+ uint32_t* amountGotten,
+ uint32_t totalExpected)
+{
+ uint32_t oldAmount = *amountGotten;
+ if (oldAmount == totalExpected) {
+ return;
+ }
+ Assert_true(oldAmount < totalExpected);
+
+ ssize_t amount = read(fileDescriptor, outputBuffer + *amountGotten, totalExpected - oldAmount);
+
+ if (amount < 1) {
+ if (EAGAIN == errno || EWOULDBLOCK == errno) {
+ return;
+ }
+ if (amount < 0) {
+ perror("broken pipe");
+ } else {
+ fprintf(stderr, "admin connection closed\n");
+ }
+ exit(0);
+ }
+ *amountGotten += amount;
+}
+
+/** return true if the header is available and false otherwise. */
+static bool getMessageHeaderFromCore(struct AngelContext* context)
+{
+ getDataFromPipe(context->inFd,
+ (uint8_t*)&context->messageHeader,
+ &context->haveMessageHeaderLen,
+ AngelChan_MessageHeader_SIZE);
+
+ return (context->haveMessageHeaderLen == AngelChan_MessageHeader_SIZE);
+}
+
+static bool getMessageBodyFromCore(struct AngelContext* context)
+{
+ getDataFromPipe(context->inFd,
+ context->message,
+ &context->haveMessageLen,
+ context->messageHeader.length);
+
+ return (context->haveMessageLen == context->messageHeader.length);
+}
+
+static void handleMessageForAngel(struct AngelContext* context)
+{
+ // TODO: handle incoming messages from the core.
+}
+
+/**
+ * handle message on the pipe from core process
+ */
+static void incomingFromCore(evutil_socket_t socket, short eventType, void* vcontext)
+{
+ struct AngelContext* context = (struct AngelContext*) vcontext;
+
+ if (!getMessageHeaderFromCore(context)) {
+ return;
+ }
+
+ // got complete header, reset message
+ context->haveMessageLen = 0;
+ if (context->syncMagic != context->messageHeader.syncMagic) {
+ fprintf(stderr, "wrong magic on admin connection\n");
+ exit(0);
+ }
+
+ uint32_t messageBodyLength = context->messageHeader.length;
+
+ if (messageBodyLength > AngelChan_MAX_MESSAGE_SIZE) {
+ fprintf(stderr, "message too large on admin connection\n");
+ exit(0);
+ }
+
+ if (!getMessageBodyFromCore(context)) {
+ return;
+ }
+
+ // got complete message, reset header. new header will reset message later
+ context->haveMessageHeaderLen = 0;
+
+ context->timeOfLastMessageFromCore = Time_currentTimeMilliseconds(context->eventBase);
+ if (context->messageHeader.channelNum == 0xffffffff) {
+ handleMessageForAngel(context);
+ return;
+ }
+ if (context->messageHeader.channelNum <= 0xffff) {
+ // message for admin connections
+ uint32_t connNumber = context->messageHeader.channelNum;
+ if (connNumber >= AngelChan_MAX_CONNECTIONS) {
+ fprintf(stderr, "got message for connection #%u, max connections is %d\n",
+ connNumber, AngelChan_MAX_CONNECTIONS);
+ return;
+ }
+
+ struct Connection* conn = &context->connections[connNumber];
+ if (-1 == conn->socket) {
+ fprintf(stderr, "got message for closed channel #%u", connNumber);
+ return;
+ }
+
+ if (0 == context->haveMessageLen) {
+ /* close channel / recv ACK for close */
+ if (NULL != conn->read) {
+ // send close ACK
+ sendToCore(context, connNumber, 0, NULL);
+ EVUTIL_CLOSESOCKET(conn->socket);
+ event_free(conn->read);
+ conn->read = NULL;
+ }
+ conn->socket = -1;
+ } else if (NULL == conn->read) {
+ /* drop message - channel is closed, wait for close ACK */
+ } else {
+ ssize_t sent;
+ sent = send(conn->socket, context->message, context->haveMessageLen, 0);
+ if (sent != (ssize_t) context->haveMessageLen) {
+ // All errors lead to closing the socket.
+ EVUTIL_CLOSESOCKET(conn->socket);
+ event_free(conn->read);
+ conn->read = NULL;
+ // send close channel
+ sendToCore(context, connNumber, 0, NULL);
+ // set conn->socket = -1 later when we recv close ACK
+ }
+ }
+ }
+}
+
+/**
+ * send message via pipe to core process
+ */
+static void sendToCore(struct AngelContext* context, uint32_t channelNum, uint32_t len, void* data)
+{
+ struct AngelChan_MessageHeader messageHeader = {
+ .syncMagic = context->syncMagic,
+ .length = len,
+ .channelNum = channelNum
+ };
+
+ // TODO: buffer writes
+
+ size_t amountWritten;
+ amountWritten = write(context->outFd, &messageHeader, AngelChan_MessageHeader_SIZE);
+ if (amountWritten != AngelChan_MessageHeader_SIZE) {
+ printf("Admin process failed to write data across pipe to main process.");
+ exit(0);
+ }
+
+ if (len > 0) {
+ amountWritten = write(context->outFd, data, len);
+ if (amountWritten != len) {
+ printf("Admin process failed to write data across pipe to main process.");
+ exit(0);
+ }
+ }
+}
+
+/**
+ * handle incoming tcp data from client connections in the admin process
+ */
+static void incomingFromClient(evutil_socket_t socket, short eventType, void* vconn)
+{
+ struct Connection* conn = (struct Connection*) vconn;
+ struct AngelContext* context = conn->context;
+ uint8_t buf[AngelChan_MAX_MESSAGE_SIZE];
+ uint32_t connNumber = conn - context->connections;
+
+ errno = 0;
+ ssize_t result = recv(socket, buf, sizeof(buf), 0);
+
+ if (result > 0) {
+ sendToCore(context, connNumber, result, buf);
+ } else if (result < 0 && (EAGAIN == errno || EWOULDBLOCK == errno)) {
+ return;
+ } else {
+ // The return value will be 0 when the peer has performed an orderly shutdown.
+ EVUTIL_CLOSESOCKET(conn->socket);
+ event_free(conn->read);
+ conn->read = NULL;
+ // send close channel
+ sendToCore(context, connNumber, 0, NULL);
+ // set conn->socket = -1 later when we recv close ACK
+ }
+}
+
+static struct Connection* newConnection(struct AngelContext* context, evutil_socket_t fd)
+{
+ struct Connection* conn = NULL;
+ for (int i = 0; i < AngelChan_MAX_CONNECTIONS; i++) {
+ if (context->connections[i].read == NULL && context->connections[i].socket == -1) {
+ conn = &context->connections[i];
+ break;
+ }
+ }
+
+ if (!conn) {
+ return NULL;
+ }
+
+ conn->read = event_new(context->eventBase, fd, EV_READ | EV_PERSIST, incomingFromClient, conn);
+ conn->socket = fd;
+ conn->context = context;
+
+ if (!conn->read) {
+ return NULL;
+ }
+
+ event_add(conn->read, NULL);
+ return conn;
+}
+
+static void acceptConn(evutil_socket_t socket, short eventType, void* vcontext)
+{
+ struct AngelContext* context = (struct AngelContext*) vcontext;
+
+ struct sockaddr_storage ss;
+ ev_socklen_t slen = sizeof(ss);
+ evutil_socket_t fd = accept(socket, (struct sockaddr*)&ss, &slen);
+ if (fd < 0) {
+ perror("acceptConn() fd < 0");
+ return;
+ } else if (fd > (evutil_socket_t) FD_SETSIZE) {
+ EVUTIL_CLOSESOCKET(fd);
+ return;
+ }
+
+ evutil_make_socket_nonblocking(fd);
+
+ struct Connection* conn = newConnection(context, fd);
+ if (!conn) {
+ EVUTIL_CLOSESOCKET(fd);
+ }
+}
+
+static void pingCore(struct AngelContext* context)
+{
+ char* ping = "d1:q4:ping4:txid4:iiiie";
+ sendToCore(context, 0xffffffff, strlen(ping), ping);
+}
+
+static void corePinger(void* vcontext)
+{
+ struct AngelContext* context = (struct AngelContext*) vcontext;
+ uint64_t now = Time_currentTimeMilliseconds(context->eventBase);
+ if (now > context->timeOfLastMessageFromCore + 1000) {
+ if (now > context->timeOfLastMessageFromCore + 10000) {
+ fprintf(stderr, "no response from core, exiting");
+ exit(1);
+ }
+ pingCore(context);
+ }
+}
+
+void Angel_start(String* pass,
+ uint8_t syncMagic[8],
+ evutil_socket_t tcpSocket,
+ int toCore,
+ int fromCore,
+ struct event_base* eventBase,
+ struct Log* logger,
+ struct Allocator* alloc)
+{
+ struct AngelContext contextStore;
+ struct AngelContext* context = &contextStore;
+ memset(context, 0, sizeof(struct AngelContext));
+
+ for (int i = 0; i < AngelChan_MAX_CONNECTIONS; i++) {
+ context->connections[i].socket = -1;
+ }
+ context->eventBase = eventBase;
+ context->inFd = fromCore;
+ context->outFd = toCore;
+ context->logger = logger;
+ context->allocator = alloc;
+ context->timeOfLastMessageFromCore = Time_currentTimeMilliseconds(context->eventBase);
+ Bits_memcpyConst(&context->syncMagic, syncMagic, 8);
+
+ context->fromCore =
+ event_new(context->eventBase,
+ context->inFd,
+ EV_READ | EV_PERSIST,
+ incomingFromCore,
+ context);
+ event_add(context->fromCore, NULL);
+
+ context->socketEvent =
+ event_new(context->eventBase, tcpSocket, EV_READ | EV_PERSIST, acceptConn, context);
+ event_add(context->socketEvent, NULL);
+
+ Timeout_setInterval(corePinger, context, 1000, eventBase, alloc);
+
+ event_base_dispatch(context->eventBase);
+}
View
33 admin/angel/Angel.h
@@ -0,0 +1,33 @@
+/* vim: set expandtab ts=4 sw=4: */
+/*
+ * You may redistribute this program 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/>.
+ */
+#ifndef Angel_H
+#define Angel_H
+
+#include "admin/angel/AngelChan.h"
+#include "benc/String.h"
+#include "memory/Allocator.h"
+
+#include <event2/event.h>
+
+void Angel_start(String* pass,
+ uint8_t syncMagic[8],
+ evutil_socket_t tcpSocket,
+ int toCore,
+ int fromCore,
+ struct event_base* eventBase,
+ struct Log* logger,
+ struct Allocator* alloc);
+
+#endif
View
105 admin/angel/AngelChan.h
@@ -0,0 +1,105 @@
+/* vim: set expandtab ts=4 sw=4: */
+/*
+ * You may redistribute this program 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/>.
+ */
+#ifndef AngelChan_H
+#define AngelChan_H
+
+#include "util/Assert.h"
+
+#include <stdint.h>
+
+/* *Channels* between angel and core process
+ *
+ * Channels are identified by number; they are opened by a non empty message,
+ * and closed by an empty message. A close on an open channel must be ACKed with
+ * an empty message before it can be reopened.
+ *
+ * Channels are in one of three states: OPEN, CLOSED, WAIT_FOR_CLOSE
+ * - OPEN:
+ * + on empty message: send close, -> CLOSED
+ * + <close>: send close -> WAIT_FOR_CLOSE
+ * + <send data>: send the non-empty data
+ * - CLOSED (start state):
+ * + ignore empty messages
+ * + on non empty message: -> OPEN
+ * + <send data>: send the non-empty data -> OPEN
+ * - WAIT_FOR_CLOSE
+ * + ignore non empty messages (optionally send close again if this channel is invalid)
+ * + on empty message: -> CLOSED
+ * + CANNOT <send data> on this channel
+ *
+ * A channel is a byte stream; the message framing must not be used to identify
+ * frames in the byte stream of a channel.
+ * (Right now bencoding messages is the way to go)
+ *
+ * If one end doesn't want to handle a channel, it responds with an immediate close
+ * on non empty messages, but it drops empty messages; such "invalid" channel are
+ * never in the OPEN state.
+ *
+ * For valid channels (channels that are not closed immediately on open) you
+ * have to keep track of the WAIT_FOR_CLOSE (and OPEN) state, but you can free all
+ * data in the CLOSED state.
+ *
+ * By not ACKing a close message you force the other end to stay in the WAIT_FOR_CLOSE
+ * state (and block reopening the channel).
+ */
+#pragma pack(4)
+struct AngelChan_MessageHeader
+{
+ /**
+ * Magic bytes which are sent at the beginning of
+ * each frame to make sure the connection is still synchronized.
+ * This is randomly generated at process startup.
+ */
+ uint64_t syncMagic;
+
+ /**
+ * length of the message data following the header
+ * zero length: close(d) communication channel
+ */
+ uint32_t length;
+
+ /**
+ * channel types:
+ * 0x00000000 - 0x0000ffff: admin connections
+ * data exchanged: continous byte stream to/from the tcp connection
+ * byte stream represents requests/responses;
+ * each request/response is bencoded
+ */
+ uint32_t channelNum;
+};
+#define AngelChan_MessageHeader_SIZE 16
+Assert_compileTime(sizeof(struct AngelChan_MessageHeader) == AngelChan_MessageHeader_SIZE);
+
+
+/* zero state ("default") is CLOSED */
+enum AngelChan_ChannelState {
+ AngelChan_ChannelState_OPEN = 1,
+ AngelChan_ChannelState_CLOSED = 0,
+ AngelChan_ChannelState_WAIT_FOR_CLOSE = 2
+};
+
+/* maximum message size for framing on the channels */
+#define AngelChan_MAX_MESSAGE_SIZE (1<<16)
+
+/* maximum message size for api requests (buffer size per active channel) */
+#define AngelChan_MAX_API_REQUEST_SIZE (1<<16)
+
+/* max connections to the admin tcp socket */
+#define AngelChan_MAX_CONNECTIONS 64
+
+/* the max size of the first initialization messages which are sent across the pipe. */
+#define AngelChan_INITIAL_CONF_BUFF_SIZE 1024
+
+#endif
View
316 admin/angel/AngelInit.c
@@ -0,0 +1,316 @@
+/* vim: set expandtab ts=4 sw=4: */
+/*
+ * You may redistribute this program 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/>.
+ */
+#include "admin/angel/Angel.h"
+#include "admin/angel/AngelChan.h"
+#include "admin/angel/Waiter.h"
+#include "benc/Dict.h"
+#include "benc/String.h"
+#include "benc/serialization/standard/StandardBencSerializer.h"
+#include "benc/serialization/BencSerializer.h"
+#include "io/ArrayReader.h"
+#include "io/ArrayWriter.h"
+#include "io/FileReader.h"
+#include "io/FileWriter.h"
+#include "memory/Allocator.h"
+#include "memory/MallocAllocator.h"
+#include "exception/Except.h"
+#include "exception/AbortHandler.h"
+//#include "exception/WriteErrorHandler.h"
+#include "util/Bits.h"
+#include "util/Assert.h"
+#include "util/Security.h"
+#include "util/Process.h"
+#include "util/Hex.h"
+#include "util/log/WriterLog.h"
+
+#include <unistd.h>
+#include <event2/event.h>
+#include <stdint.h>
+#include <errno.h>
+
+/**
+ * Initialize the core.
+ *
+ * @param coreBinaryPath the path to the core binary.
+ * @param toCore a pointer to an int which will be set to the
+ * file descriptor for writing to the core.
+ * @param fromCore a pointer to an int which will be set to the
+ * file descriptor for reading from the core.
+ * @param alloc an allocator.
+ * @param eh an exception handler in case something goes wrong.
+ */
+static void initCore(char* coreBinaryPath,
+ int* toCore,
+ int* fromCore,
+ struct Allocator* alloc,
+ struct Except* eh)
+{
+ int pipes[2][2];
+ if (pipe(pipes[0]) || pipe(pipes[1])) {
+ Except_raise(eh, -1, "Failed to create pipes [%s]", strerror(errno));
+ }
+
+ // Pipes used in the core process.
+ #define TO_ANGEL (pipes[1][1])
+ #define FROM_ANGEL (pipes[0][0])
+
+ // Pipes used in the angel process (here).
+ #define TO_CORE (pipes[0][1])
+ #define FROM_CORE (pipes[1][0])
+
+ char toAngel[32];
+ char fromAngel[32];
+ snprintf(toAngel, 32, "%u", TO_ANGEL);
+ snprintf(fromAngel, 32, "%u", FROM_ANGEL);
+ char* args[] = { "core", toAngel, fromAngel, NULL };
+
+ FILE* file;
+ if ((file = fopen(coreBinaryPath, "r")) != NULL) {
+ fclose(file);
+ } else {
+ Except_raise(eh, -1, "Can't open core executable [%s] for reading.", coreBinaryPath);
+ }
+
+ if (Process_spawn(coreBinaryPath, args)) {
+ Except_raise(eh, -1, "Failed to spawn core process.");
+ }
+
+ *toCore = TO_CORE;
+ *fromCore = FROM_CORE;
+}
+
+static void sendConfToCore(int toCore,
+ struct Allocator* alloc,
+ Dict* config,
+ struct Except* eh,
+ struct Log* logger)
+{
+ #define CONFIG_BUFF_SIZE 1024
+ uint8_t buff[CONFIG_BUFF_SIZE] = {0};
+ struct Writer* writer = ArrayWriter_new(buff, CONFIG_BUFF_SIZE - 1, alloc);
+ if (StandardBencSerializer_get()->serializeDictionary(writer, config)) {
+ Except_raise(eh, -1, "Failed to serialize pre-configuration for core.");
+ }
+ write(toCore, buff, writer->bytesWritten(writer));
+ Log_keys(logger, "Sent [%s] to core.", buff);
+}
+
+/**
+ * @param bindAddr a string representation of the address to bind to, if 0.0.0.0:0,
+ * an address will be chosen automatically.
+ * @param alloc an allocator to build the output.
+ * @param eh an exception handler which will be triggered if anything goes wrong.
+ * @param sock a pointer which will be set to the bound socket.
+ * @return a dictionary containing: "addr" which is a string representation of the ip address
+ * which was bound and "port" which is the bound port as an integer.
+ */
+static String* bindListener(String* bindAddr,
+ struct Allocator* alloc,
+ struct Except* eh,
+ evutil_socket_t* sock)
+{
+ struct sockaddr_storage addr;
+ int addrLen = sizeof(struct sockaddr_storage);
+ memset(&addr, 0, sizeof(struct sockaddr_storage));
+ if (evutil_parse_sockaddr_port(bindAddr->bytes, (struct sockaddr*) &addr, &addrLen)) {
+ Except_raise(eh, -1, "Unable to parse [%s] as an ip address and port, "
+ "eg: 127.0.0.1:11234", bindAddr->bytes);
+ }
+
+ if (!addr.ss_family) {
+ addr.ss_family = AF_INET;
+ // Apple gets mad if the length is wrong.
+ addrLen = sizeof(struct sockaddr_in);
+ }
+
+ evutil_socket_t listener = socket(addr.ss_family, SOCK_STREAM, 0);
+ if (listener < 0) {
+ Except_raise(eh, -1, "Failed to allocate socket() [%s]", strerror(errno));
+ }
+
+ evutil_make_listen_socket_reuseable(listener);
+
+ if (bind(listener, (struct sockaddr*) &addr, addrLen) < 0) {
+ int err = errno;
+ EVUTIL_CLOSESOCKET(listener);
+ Except_raise(eh, -1, "Failed to bind() socket [%s]", strerror(err));
+ }
+
+ if (getsockname(listener, (struct sockaddr*) &addr, (ev_socklen_t*) &addrLen)) {
+ int err = errno;
+ EVUTIL_CLOSESOCKET(listener);
+ Except_raise(eh, -1, "Failed to get socket name [%s]", strerror(err));
+ }
+
+ if (listen(listener, 16) < 0) {
+ int err = errno;
+ EVUTIL_CLOSESOCKET(listener);
+ Except_raise(eh, -1, "Failed to listen on socket [%s]", strerror(err));
+ }
+
+ #define ADDR_BUFF_SZ 64
+ char addrStr[ADDR_BUFF_SZ] = {0};
+ uint16_t port;
+ switch(addr.ss_family) {
+ case AF_INET:
+ evutil_inet_ntop(AF_INET,
+ &(((struct sockaddr_in*)&addr)->sin_addr),
+ addrStr,
+ ADDR_BUFF_SZ);
+ port = ((struct sockaddr_in*)&addr)->sin_port;
+ break;
+ case AF_INET6:
+ evutil_inet_ntop(AF_INET6,
+ &(((struct sockaddr_in6*)&addr)->sin6_addr),
+ addrStr + 1,
+ ADDR_BUFF_SZ - 2);
+ addrStr[0] = '[';
+ addrStr[strlen(addrStr)] = ']';
+ port = ((struct sockaddr_in6*)&addr)->sin6_port;
+ break;
+ default:
+ Assert_always(false);
+ }
+ port = Endian_bigEndianToHost16(port);
+ snprintf(addrStr + strlen(addrStr), 48 - strlen(addrStr), ":%u", port);
+
+ *sock = listener;
+ return String_new(addrStr, alloc);
+}
+
+static Dict* getInitialConfigResponse(int fromCore,
+ struct event_base* eventBase,
+ struct Allocator* alloc,
+ struct Except* eh)
+{
+ uint8_t buff[AngelChan_INITIAL_CONF_BUFF_SIZE] = {0};
+ uint32_t amountRead =
+ Waiter_getData(buff, AngelChan_INITIAL_CONF_BUFF_SIZE, fromCore, eventBase, eh);
+ if (amountRead == AngelChan_INITIAL_CONF_BUFF_SIZE) {
+ Except_raise(eh, -1, "initial config exceeds INITIAL_CONF_BUFF_SIZE");
+ }
+
+ struct Reader* reader = ArrayReader_new(buff, AngelChan_INITIAL_CONF_BUFF_SIZE, alloc);
+ Dict* config = Dict_new(alloc);
+ if (StandardBencSerializer_get()->parseDictionary(reader, alloc, config)) {
+ Except_raise(eh, -1, "Failed to parse initial configuration.");
+ }
+
+ return config;
+}
+
+/**
+ * Input:
+ * {
+ * "admin": {
+ * "core": "/path/to/core/binary",
+ * "bind": "127.0.0.1:12345",
+ * "pass": "12345adminsocketpassword",
+ * "user": "setUidToThisUser"
+ * }
+ * }
+ * for example:
+ * d5:admind4:core30:./build/admin/angel/cjdns-core4:bind15:127.0.0.1:123454:pass4:abcdee
+ *
+/home/user/wrk/cjdns/build/admin/angel/cjdns-core
+ * "user" is optional, if set the angel will setuid() that user's uid.
+ */
+int AngelInit_main(int argc, char** argv)
+{
+ struct Except* eh = AbortHandler_INSTANCE;
+
+ int inFromClientNo;
+ int outToClientNo;
+ if (argc < 3 || (inFromClientNo = atoi(argv[2])) == 0) {
+ inFromClientNo = STDIN_FILENO;
+ }
+ if (argc < 4 || (outToClientNo = atoi(argv[3])) == 0) {
+ outToClientNo = STDOUT_FILENO;
+ }
+
+ struct Allocator* alloc = MallocAllocator_new(1<<20);
+ struct event_base* eventBase = event_base_new();
+
+ struct Writer* logWriter = FileWriter_new(stdout, alloc);
+ struct Log* logger = WriterLog_new(logWriter, alloc);
+
+ Log_debug(logger, "Getting pre-configuration from client");
+
+ #define CONFIG_BUFF_SIZE 1024
+ uint8_t buff[CONFIG_BUFF_SIZE] = {0};
+ Waiter_getData(buff, CONFIG_BUFF_SIZE, inFromClientNo, eventBase, eh);
+
+ Log_debug(logger, "Finished getting pre-configuration from client");
+
+ struct Reader* reader = ArrayReader_new(buff, CONFIG_BUFF_SIZE, alloc);
+ Dict config;
+ if (StandardBencSerializer_get()->parseDictionary(reader, alloc, &config)) {
+ Except_raise(eh, -1, "Failed to parse configuration.");
+ }
+
+ Dict* admin = Dict_getDict(&config, String_CONST("admin"));
+ String* core = Dict_getString(admin, String_CONST("core"));
+ String* bind = Dict_getString(admin, String_CONST("bind"));
+ String* pass = Dict_getString(admin, String_CONST("pass"));
+ String* user = Dict_getString(admin, String_CONST("user"));
+
+ if (!core || !bind || !pass) {
+ Except_raise(eh, -1, "missing configuration params in preconfig. [%s]", buff);
+ }
+
+ evutil_socket_t tcpSocket;
+ String* boundAddr = bindListener(bind, alloc, eh, &tcpSocket);
+
+ int toCore;
+ int fromCore;
+ Log_debug(logger, "Initializing core [%s]", core->bytes);
+ initCore(core->bytes, &toCore, &fromCore, alloc, eh);
+ Log_debug(logger, "Sending pre-configuration to core.");
+ sendConfToCore(toCore, alloc, &config, eh, logger);
+ Dict* configResp = getInitialConfigResponse(fromCore, eventBase, alloc, eh);
+ Dict* angelResp = Dict_getDict(configResp, String_CONST("angel"));
+ String* syncMagicStr = Dict_getString(angelResp, String_CONST("syncMagic"));
+
+ // The client doesn't care about angel<-> core matters.
+ Dict_remove(configResp, String_CONST("angel"));
+
+ if (!syncMagicStr || syncMagicStr->len != 16) {
+ Except_raise(eh, -1, "didn't get proper syncMagic from core.");
+ }
+ uint8_t syncMagic[8];
+ Hex_decode(syncMagic, 8, (uint8_t*)syncMagicStr->bytes, 16);
+
+ Dict* adminResp = Dict_getDict(configResp, String_CONST("admin"));
+ if (!adminResp) {
+ adminResp = Dict_new(alloc);
+ Dict_putDict(configResp, String_CONST("admin"), adminResp, alloc);
+ }
+
+ Dict_putString(adminResp, String_CONST("bind"), boundAddr, alloc);
+
+ struct Writer* toClientWriter = ArrayWriter_new(buff, CONFIG_BUFF_SIZE, alloc);
+ if (StandardBencSerializer_get()->serializeDictionary(toClientWriter, configResp)) {
+ Except_raise(eh, -1, "Failed to serialize response");
+ }
+ write(outToClientNo, buff, toClientWriter->bytesWritten(toClientWriter));
+ buff[toClientWriter->bytesWritten(toClientWriter)] = 0;
+ Log_keys(logger, "Sent [%s] to client.", buff);
+ if (user) {
+ Security_setUser(user->bytes, NULL, eh);
+ }
+
+ Angel_start(pass, syncMagic, tcpSocket, toCore, fromCore, eventBase, logger, alloc);
+ return 0;
+}
View
36 admin/angel/AngelInit.h
@@ -0,0 +1,36 @@
+/* vim: set expandtab ts=4 sw=4: */
+/*
+ * You may redistribute this program 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/>.
+ */
+#ifndef AngelInit_H
+#define AngelInit_H
+
+/**
+ * Input:
+ * {
+ * "admin": {
+ * "core": "/path/to/core/binary",
+ * "bind": "127.0.0.1:12345",
+ * "pass": "12345adminsocketpassword",
+ * "user": "setUidToThisUser"
+ * }
+ * }
+ * for example:
+ * d5:admind4:core30:./build/admin/angel/cjdns-core4:bind15:127.0.0.1:123454:pass4:abcdee
+ *
+/home/user/wrk/cjdns/build/admin/angel/cjdns-core
+ * "user" is optional, if set the angel will setuid() that user's uid.
+ */
+int AngelInit_main(int argc, char** argv);
+
+#endif
View
82 admin/angel/CMakeLists.txt
@@ -0,0 +1,82 @@
+# You may redistribute this program 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/>.
+
+add_library(cjdns-waiter
+ Waiter.c
+)
+
+add_library(cjdns-angel
+ Angel.c
+ AngelInit.c
+)
+
+target_link_libraries(cjdns-angel
+ cjdbenc
+ cjdbenc_StandardBencSerializer
+ cjdmemory
+ ${LIBEVENT2_LIBRARIES}
+ cjdns-security
+ cjdns-process
+ cjdns-waiter
+ util # Hex_decode()
+ cjdns-util-log-writer
+)
+
+add_library(cjdns-core
+ Core.c
+ Core_admin.c
+)
+
+target_link_libraries(cjdns-core
+ crypto
+ interface
+ switch
+ dht
+ dhtcore
+ cjdbenc
+ cjdbenc_StandardBencSerializer
+ cjdmemory
+ cjdns-admin
+ cjdnet
+ ${LIBEVENT2_LIBRARIES}
+ ${PLATFORM_LIBRARIES}
+ cjdns-waiter
+ cjdns-util-log-writer
+ cjdns-admin-logger
+ cjdns-util-log-indirect
+)
+
+add_executable(cjdns
+ Cjdns.c
+)
+
+target_link_libraries(cjdns cjdns-angel cjdns-core)
+
+add_executable(cjdroute2
+ cjdroute2.c
+)
+
+target_link_libraries(cjdroute2
+ cjdns-admin-client
+ crypto
+ cjdns-crypto-bench
+ cjdbenc_StandardBencSerializer
+ cjdbenc_JsonBencSerializer
+ cjdns-process
+ cjdns-waiter
+ cjdns-util-log-writer
+)
+
+
+
+enable_testing()
+#add_subdirectory(test)
View
32 admin/angel/Cjdns.c
@@ -0,0 +1,32 @@
+/* vim: set expandtab ts=4 sw=4: */
+/*
+ * You may redistribute this program 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/>.
+ */
+#include "admin/angel/AngelInit.h"
+#include "admin/angel/Core.h"
+
+#include <stdio.h>
+#include <unistd.h>
+
+int main(int argc, char** argv)
+{
+ if (isatty(STDIN_FILENO) || argc < 2) {
+ // Fall through.
+ } else if (!strcmp("angel", argv[1])) {
+ return AngelInit_main(argc, argv);
+ } else if (!strcmp("core", argv[1])) {
+ return Core_main(argc, argv);
+ }
+ printf("This is internal to cjdns, it should not be started manually.\n");
+ return -1;
+}
View
313 admin/angel/Core.c
@@ -0,0 +1,313 @@
+/* vim: set expandtab ts=4 sw=4: */
+/*
+ * You may redistribute this program 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/>.
+ */
+
+#define _POSIX_SOURCE // fdopen()
+
+#include "admin/Admin.h"
+#include "admin/AdminLog.h"
+#include "admin/angel/AngelChan.h"
+#include "admin/angel/Waiter.h"
+#include "admin/angel/Core.h"
+#include "admin/angel/Core_admin.h"
+#include "admin/AuthorizedPasswords.h"
+#include "benc/Int.h"
+#include "benc/serialization/BencSerializer.h"
+#include "benc/serialization/standard/StandardBencSerializer.h"
+#include "crypto/Crypto.h"
+#include "dht/ReplyModule.h"
+#include "dht/SerializationModule.h"
+#include "dht/dhtcore/RouterModule_admin.h"
+#include "exception/AbortHandler.h"
+#include "exception/WriteErrorHandler.h"
+#include "interface/UDPInterface_admin.h"
+#include "interface/TUNConfigurator.h"
+#include "interface/TUNInterface.h"
+#include "io/ArrayReader.h"
+#include "io/FileWriter.h"
+#include "io/Reader.h"
+#include "io/Writer.h"
+#include "memory/Allocator.h"
+#include "memory/MallocAllocator.h"
+#include "net/Ducttape.h"
+#include "net/DefaultInterfaceController.h"
+#include "net/SwitchPinger.h"
+#include "net/SwitchPinger_admin.h"
+#include "switch/SwitchCore.h"
+#include "util/log/WriterLog.h"
+#include "util/log/IndirectLog.h"
+#include "util/Security_admin.h"
+
+#include <crypto_scalarmult_curve25519.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+// Failsafe: abort if more than 2^22 bytes are allocated (4MB)
+#define ALLOCATOR_FAILSAFE (1<<22)
+
+/**
+ * The worst possible packet overhead.
+ * assuming the packet needs to be handed off to another node
+ * because we have no route to the destination.
+ * and the CryptoAuths to both the destination and the handoff node are both timed out.
+ */
+#define WORST_CASE_OVERHEAD ( \
+ /* TODO: Headers_IPv4_SIZE */ 20 \
+ + Headers_UDPHeader_SIZE \
+ + 4 /* Nonce */ \
+ + 16 /* Poly1305 authenticator */ \
+ + Headers_SwitchHeader_SIZE \
+ + Headers_CryptoAuth_SIZE \
+ + Headers_IP6Header_SIZE \
+ + Headers_CryptoAuth_SIZE \
+)
+
+/** The default MTU, assuming the external MTU is 1492 (common for PPPoE DSL) */
+#define DEFAULT_MTU ( \
+ 1492 \
+ - WORST_CASE_OVERHEAD \
+ + Headers_IP6Header_SIZE /* The OS subtracts the IP6 header. */ \
+ + Headers_CryptoAuth_SIZE /* Linux won't let set the MTU below 1280.
+ TODO: make sure we never hand off to a node for which the CA session is expired. */ \
+)
+
+static void parsePrivateKey(uint8_t privateKey[32],
+ struct Address* addr,
+ struct Except* eh)
+{
+ crypto_scalarmult_curve25519_base(addr->key, privateKey);
+ AddressCalc_addressForPublicKey(addr->ip6.bytes, addr->key);
+ if (addr->ip6.bytes[0] != 0xFC) {
+ Except_raise(eh, -1, "Ip address outside of the FC00/8 range, invalid private key.");
+ }
+}
+
+static void adminPing(Dict* input, void* vadmin, String* txid)
+{
+ Dict d = Dict_CONST(String_CONST("q"), String_OBJ(String_CONST("pong")), NULL);
+ Admin_sendMessage(&d, txid, (struct Admin*) vadmin);
+}
+
+struct MemoryContext
+{
+ struct Allocator* allocator;
+ struct Admin* admin;
+};
+
+static void adminMemory(Dict* input, void* vcontext, String* txid)
+{
+ struct MemoryContext* context = vcontext;
+ Dict d = Dict_CONST(
+ String_CONST("bytes"), Int_OBJ(MallocAllocator_bytesAllocated(context->allocator)), NULL
+ );
+ Admin_sendMessage(&d, txid, context->admin);
+}
+
+static void adminExit(Dict* input, void* vcontext, String* txid)
+{
+ exit(1);
+}
+
+static Dict* getInitialConfig(int fromAngel,
+ struct event_base* eventBase,
+ struct Allocator* alloc,
+ struct Except* eh)
+{
+ uint8_t buff[AngelChan_INITIAL_CONF_BUFF_SIZE] = {0};
+ uint32_t amountRead =
+ Waiter_getData(buff, AngelChan_INITIAL_CONF_BUFF_SIZE, fromAngel, eventBase, eh);
+ if (amountRead == AngelChan_INITIAL_CONF_BUFF_SIZE) {
+ Except_raise(eh, -1, "initial config exceeds INITIAL_CONF_BUFF_SIZE");
+ }
+
+ struct Reader* reader = ArrayReader_new(buff, AngelChan_INITIAL_CONF_BUFF_SIZE, alloc);
+ Dict* config = Dict_new(alloc);
+ if (StandardBencSerializer_get()->parseDictionary(reader, alloc, config)) {
+ Except_raise(eh, -1, "Failed to parse initial configuration.");
+ }
+
+ return config;
+}
+
+static void sendResponse(int toAngel, uint8_t syncMagic[8])
+{
+ char buff[64] =
+ "d"
+ "5:angel" "d"
+ "9:syncMagic" "16:\1\1\2\3\4\5\6\7\1\1\2\3\4\5\6\7"
+ "e"
+ "e";
+
+ uint32_t length = strlen(buff);
+ char* location = strstr(buff, "\1\1\2\3\4\5\6\7\1\1\2\3\4\5\6\7");
+ Assert_true(location > buff);
+ Hex_encode((uint8_t*)location, 16, syncMagic, 8);
+ write(toAngel, buff, length);
+}
+
+void Core_initTunnel(String* desiredDeviceName,
+ uint8_t ipAddr[16],
+ uint8_t addressPrefix,
+ struct Ducttape* dt,
+ struct Log* logger,
+ struct event_base* eventBase,
+ struct Allocator* alloc,
+ struct Except* eh)
+{
+ Log_debug(logger, "Initializing TUN device [%s]",
+ (desiredDeviceName) ? desiredDeviceName->bytes : "<auto>");
+ char assignedTunName[TUNConfigurator_IFNAMSIZ];
+ void* tunPtr = TUNConfigurator_initTun(((desiredDeviceName) ? desiredDeviceName->bytes : NULL),
+ assignedTunName,
+ logger,
+ eh);
+
+ TUNConfigurator_setIpAddress(assignedTunName, ipAddr, addressPrefix, logger, eh);
+ TUNConfigurator_setMTU(assignedTunName, DEFAULT_MTU, logger, eh);
+ struct TUNInterface* tun = TUNInterface_new(tunPtr, eventBase, alloc);
+ Ducttape_setUserInterface(dt, &tun->iface);
+}
+
+/*
+ * This process is started with 2 parameters, they must all be numeric in base 10.
+ * toAngel the pipe which is used to send data back to the angel process.
+ * fromAngel the pipe which is used to read incoming data from the angel.
+ *
+ * Upon initialization, this process will wait for an initial configuration to be sent to
+ * it and then it will send an initial response.
+ */
+int Core_main(int argc, char** argv)
+{
+ uint8_t syncMagic[8];
+ randombytes(syncMagic, 8);
+
+ struct Except* eh = AbortHandler_INSTANCE;
+ int toAngel;
+ int fromAngel;
+ if (argc != 4
+ || !(toAngel = atoi(argv[2]))
+ || !(fromAngel = atoi(argv[3])))
+ {
+ Except_raise(eh, -1, "This is internal to cjdns and shouldn't started manually.");
+ }
+
+ struct Allocator* alloc = MallocAllocator_new(ALLOCATOR_FAILSAFE);
+
+
+ FILE* toAngelF = fdopen(toAngel, "w");
+ Assert_always(toAngelF != NULL);
+ struct Writer* toAngelWriter = FileWriter_new(toAngelF, alloc);
+ eh = WriteErrorHandler_new(toAngelWriter, alloc);
+
+ struct event_base* eventBase = event_base_new();
+
+ // -------------------- Setup the Pre-Logger ---------------------- //
+ struct Writer* logWriter = FileWriter_new(stdout, alloc);
+ struct Log* preLogger = WriterLog_new(logWriter, alloc);
+ struct IndirectLog* indirectLogger = IndirectLog_new(alloc);
+ indirectLogger->wrappedLog = preLogger;
+ struct Log* logger = &indirectLogger->pub;
+
+
+ Dict* config = getInitialConfig(fromAngel, eventBase, alloc, eh);
+ String* privateKeyHex = Dict_getString(config, String_CONST("privateKey"));
+ Dict* adminConf = Dict_getDict(config, String_CONST("admin"));
+ String* pass = Dict_getString(adminConf, String_CONST("pass"));
+ if (!pass || !privateKeyHex) {
+ Except_raise(eh, -1, "Expected 'pass' and 'privateKey' in configuration.");
+ }
+ Log_keys(indirectLogger, "Starting core with admin password [%s]", pass->bytes);
+ uint8_t privateKey[32];
+ if (privateKeyHex->len != 64
+ || Hex_decode(privateKey, 32, (uint8_t*) privateKeyHex->bytes, 64) != 32)
+ {
+ Except_raise(eh, -1, "privateKey must be 64 bytes of hex.");
+ }
+
+ sendResponse(toAngel, syncMagic);