Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Refactored admin/Configurator adding AdminClient and added admin test.

  • Loading branch information...
commit 47638f05ba777219d51013f2dbe48d12846219ad 1 parent 25cf60e
Caleb James DeLisle authored
View
39 admin/Admin.c
@@ -259,7 +259,19 @@ static void inFromChild(evutil_socket_t socket, short eventType, void* vcontext)
}
if (!admin->initialized) {
- admin->initialized = true;
+ if (amount != sizeof(struct sockaddr_storage) + sizeof(int) + 8) {
+ Log_error(admin->logger, "unexpected length");
+ } else if (memcmp(buffer, "abcd", 4)) {
+ Log_error(admin->logger, "bad magic");
+ } else if (memcmp(&buffer[sizeof(struct sockaddr_storage) + sizeof(int) + 4], "wxyz", 4)) {
+ Log_error(admin->logger, "bad magic");
+ } else {
+ Bits_memcpyConst(&admin->addressLength, &buffer[4], sizeof(int));
+ Bits_memcpyConst(&admin->address,
+ &buffer[4 + sizeof(int)],
+ sizeof(struct sockaddr_storage));
+ admin->initialized = true;
+ }
event_base_loopbreak(admin->eventBase);
return;
}
@@ -436,13 +448,27 @@ static void child(struct sockaddr_storage* addr,
event_add(context->dataFromParent, NULL);
+ if (!addr->ss_family) {
+ addr->ss_family = AF_INET;
+ }
+
evutil_socket_t listener = socket(addr->ss_family, SOCK_STREAM, 0);
+
+ if (listener < 0) {
+ perror("socket()");
+ }
+
evutil_make_listen_socket_reuseable(listener);
if (bind(listener, (struct sockaddr*) addr, addrLen) < 0) {
perror("bind");
return;
}
+
+ if (getsockname(listener, (struct sockaddr*) addr, (socklen_t*) &addrLen)) {
+ perror("getsockname()");
+ }
+
if (listen(listener, 16)<0) {
perror("listen");
return;
@@ -458,8 +484,13 @@ static void child(struct sockaddr_storage* addr,
exit(-1);
}
- // Bump the router process to indicate that we're initialized.
- write(context->outFd, "ready", strlen("ready"));
+ // Write back the sockaddr_storage struct so the other end knows our port.
+ uint8_t buff[sizeof(struct sockaddr_storage) + sizeof(int) + 8];
+ Bits_memcpyConst(buff, "abcd", 4);
+ Bits_memcpyConst(&buff[4], &addrLen, sizeof(int));
+ Bits_memcpyConst(&buff[4 + sizeof(int)], addr, sizeof(struct sockaddr_storage));
+ Bits_memcpyConst(&buff[4 + sizeof(int) + sizeof(struct sockaddr_storage)], "wxyz", 4);
+ write(context->outFd, buff, sizeof(buff));
event_base_dispatch(context->eventBase);
}
@@ -570,7 +601,7 @@ struct Admin* Admin_new(struct sockaddr_storage* addr,
int outFd = pipes[!isChild][1];
close(pipes[isChild][1]);
- if (!isChild) {
+ if (isChild) {
// Set the process group so that children will not
// become orphaned if the parent gets signal 11 err um 9.
setpgid(0, pgid);
View
271 admin/AdminClient.c
@@ -0,0 +1,271 @@
+/* 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/AdminClient.h"
+#include "benc/serialization/BencSerializer.h"
+#include "benc/serialization/standard/StandardBencSerializer.h"
+#include "io/Reader.h"
+#include "io/ArrayReader.h"
+#include "io/Writer.h"
+#include "io/ArrayWriter.h"
+#include "util/Endian.h"
+#include "util/Hex.h"
+
+#include <crypto_hash_sha256.h>
+#include <unistd.h>
+#include <errno.h>
+
+struct AdminClient
+{
+ /**
+ * This is unused but if there are ever public fields in AdminClient,
+ * this will be replaced with those and this structure will be moved to AdminClient.h
+ * currently it just prevents an empty struct error.
+ */
+ int placeholder;
+};
+
+struct Result
+{
+ struct AdminClient_Result public;
+ struct Context* ctx;
+ struct Allocator* alloc;
+};
+
+struct Context
+{
+ struct AdminClient public;
+ struct Result* result;
+ evutil_socket_t socket;
+ struct event* socketEvent;
+ struct event_base* eventBase;
+ struct Log* logger;
+ String* password;
+};
+
+static int calculateAuth(Dict* message,
+ String* password,
+ String* cookieStr,
+ struct Allocator* alloc)
+{
+ // Calculate the hash of the password.
+ String* hashHex = String_newBinary(NULL, 64, alloc);
+ uint8_t passAndCookie[64];
+ uint32_t cookie = (cookieStr != NULL) ? strtoll(cookieStr->bytes, NULL, 10) : 0;
+ snprintf((char*) passAndCookie, 64, "%s%u", password->bytes, cookie);
+ uint8_t hash[32];
+ crypto_hash_sha256(hash, passAndCookie, strlen((char*) passAndCookie));
+ Hex_encode((uint8_t*)hashHex->bytes, 64, hash, 32);
+
+ Dict_putString(message, String_new("hash", alloc), hashHex, alloc);
+ Dict_putString(message, String_new("cookie", alloc), cookieStr, alloc);
+
+ // serialize the message with the password hash
+ uint8_t buffer[AdminClient_MAX_MESSAGE_SIZE];
+ struct Writer* writer = ArrayWriter_new(buffer, AdminClient_MAX_MESSAGE_SIZE, alloc);
+ if (StandardBencSerializer_get()->serializeDictionary(writer, message)) {
+ return -1;
+ }
+ int length = writer->bytesWritten(writer);
+
+ // calculate the hash of the message with the password hash
+ crypto_hash_sha256(hash, buffer, length);
+
+ // swap the hash of the message with the password hash into the location
+ // where the password hash was.
+ Hex_encode((uint8_t*)hashHex->bytes, 64, hash, 32);
+ return 0;
+}
+
+static void done(struct Context* ctx, enum AdminClient_Error err)
+{
+ ctx->result->public.err = err;
+ event_base_loopexit(ctx->eventBase, NULL);
+}
+
+static void timeout(evutil_socket_t socket, short eventType, void* vcontext)
+{
+ done((struct Context*) vcontext, AdminClient_Error_TIMEOUT);
+}
+
+static void doCall(Dict* message, struct Result* res, bool getCookie)
+{
+ String* cookie = NULL;
+ if (!getCookie) {
+ Dict gc = Dict_CONST(String_CONST("q"), String_OBJ(String_CONST("cookie")), NULL);
+ doCall(&gc, res, true);
+ if (res->public.err != AdminClient_Error_NONE) {
+ return;
+ }
+ cookie = Dict_getString(res->public.responseDict, String_CONST("cookie"));
+ if (!cookie) {
+ res->public.err = AdminClient_Error_NO_COOKIE;
+ }
+ }
+
+ struct Writer* writer =
+ ArrayWriter_new(res->public.messageBytes, AdminClient_MAX_MESSAGE_SIZE, res->alloc);
+ if (StandardBencSerializer_get()->serializeDictionary(writer, message)) {
+ res->public.err = AdminClient_Error_SERIALIZATION_FAILED;
+ return;
+ }
+
+ if (!getCookie) {
+ calculateAuth(message, res->ctx->password, cookie, res->alloc);
+
+ writer = ArrayWriter_new(res->public.messageBytes, AdminClient_MAX_MESSAGE_SIZE, res->alloc);
+ if (StandardBencSerializer_get()->serializeDictionary(writer, message)) {
+ res->public.err = AdminClient_Error_SERIALIZATION_FAILED;
+ return;
+ }
+ }
+
+ Log_keys1(res->ctx->logger, "Sending message: %s", buff);
+ send(res->ctx->socket, res->public.messageBytes, writer->bytesWritten(writer), 0);
+
+ struct event* timeoutEvent = evtimer_new(res->ctx->eventBase, timeout, res);
+ evtimer_add(timeoutEvent, (&(struct timeval) { .tv_sec = 5, .tv_usec = 0 }));
+
+ event_base_dispatch(res->ctx->eventBase);
+
+ evtimer_del(timeoutEvent);
+}
+
+static void incoming(evutil_socket_t socket, short eventType, void* vcontext)
+{
+ struct Context* ctx = vcontext;
+ // Since this is a blocking api, one result per context.
+ struct Result* res = ctx->result;
+
+ ssize_t length = recv(socket, res->public.messageBytes, AdminClient_MAX_MESSAGE_SIZE, 0);
+ if (length == AdminClient_MAX_MESSAGE_SIZE) {
+ while (length) {
+ // purge the socket.
+ length = recv(socket, res->public.messageBytes, AdminClient_MAX_MESSAGE_SIZE, 0);
+ }
+ done(ctx, AdminClient_Error_OVERLONG_RESPONSE);
+ return;
+ }
+ if (length < 1) {
+ if (length < 0) {
+ done(ctx, AdminClient_Error_ERROR_READING_FROM_SOCKET);
+ } else {
+ done(ctx, AdminClient_Error_SOCKET_NOT_READY);
+ }
+ return;
+ }
+
+ struct Reader* reader = ArrayReader_new(res->public.messageBytes, length, res->alloc);
+ Dict* d = Dict_new(res->alloc);
+ if (StandardBencSerializer_get()->parseDictionary(reader, res->alloc, d)) {
+ done(ctx, AdminClient_Error_DESERIALIZATION_FAILED);
+ return;
+ }
+ res->public.responseDict = d;
+
+ done(ctx, AdminClient_Error_NONE);
+}
+
+struct AdminClient_Result* AdminClient_rpcCall(String* function,
+ Dict* args,
+ struct AdminClient* client,
+ struct Allocator* alloc)
+{
+ struct Context* ctx = (struct Context*) client;
+ Dict a = (args) ? *args : NULL;
+ Dict message = Dict_CONST(
+ String_CONST("q"), String_OBJ(String_CONST("auth")), Dict_CONST(
+ String_CONST("aq"), String_OBJ(function), Dict_CONST(
+ String_CONST("args"), Dict_OBJ(&a), NULL
+ )));
+ struct Result* res = alloc->clone(sizeof(struct Result), alloc, &(struct Result) {
+ .public = {
+ .err = AdminClient_Error_NONE
+ },
+ .ctx = ctx,
+ .alloc = alloc
+ });
+ ctx->result = res;
+ doCall(&message, res, false);
+ return &res->public;
+}
+
+char* AdminClient_errorString(enum AdminClient_Error err)
+{
+ switch (err) {
+ case AdminClient_Error_NONE:
+ return "Success";
+ case AdminClient_Error_OVERLONG_RESPONSE:
+ return "Overlong resonse message";
+ case AdminClient_Error_ERROR_READING_FROM_SOCKET:
+ return "Error reading from socket, check errno.";
+ case AdminClient_Error_SOCKET_NOT_READY:
+ return "Socket not ready for reading";
+ case AdminClient_Error_DESERIALIZATION_FAILED:
+ return "Failed to deserialize response";
+ case AdminClient_Error_SERIALIZATION_FAILED:
+ return "Failed to serialize request";
+ case AdminClient_Error_TIMEOUT:
+ return "Timed out waiting for a response";
+ case AdminClient_Error_NO_COOKIE:
+ return "Cookie request returned with no cookie";
+ default:
+ return "Internal error";
+ };
+}
+
+static void disconnect(void* vcontext)
+{
+ struct Context* context = vcontext;
+ close(context->socket);
+ event_del(context->socketEvent);
+}
+
+struct AdminClient* AdminClient_new(struct sockaddr_storage* addr,
+ int addrLen,
+ String* adminPassword,
+ struct event_base* eventBase,
+ struct Log* logger,
+ struct Allocator* alloc)
+{
+ struct Context* context = alloc->clone(sizeof(struct Context), alloc, &(struct Context) {
+ .eventBase = eventBase,
+ .logger = logger,
+ .password = adminPassword,
+ });
+
+ context->socket = socket(addr->ss_family, SOCK_STREAM, 0);
+
+ if (context->socket < 0) {
+ Log_error1(logger, "Failed to allocate socket, errno=%d", errno);
+ return NULL;
+ }
+
+ evutil_make_listen_socket_reuseable(context->socket);
+
+ if (connect(context->socket, (struct sockaddr*)addr, addrLen)) {
+ Log_error1(logger, "Failed to connect, errno=%d", errno);
+ return NULL;
+ }
+
+ evutil_make_socket_nonblocking(context->socket);
+
+ context->socketEvent =
+ event_new(context->eventBase, context->socket, EV_READ | EV_PERSIST, incoming, context);
+ event_add(context->socketEvent, NULL);
+
+ alloc->onFree(disconnect, context, alloc);
+
+ return &context->public;
+}
View
89 admin/AdminClient.h
@@ -0,0 +1,89 @@
+/* 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 AdminClient_H
+#define AdminClient_H
+
+#include "benc/String.h"
+#include "benc/Dict.h"
+#include "memory/Allocator.h"
+#include "util/Log.h"
+
+#include <event2/event.h>
+
+enum AdminClient_Error
+{
+ /** Success. */
+ AdminClient_Error_NONE = 0,
+
+ /** Response was longer than max message size. */
+ AdminClient_Error_OVERLONG_RESPONSE,
+
+ /** Error on recv(), causes result.errno to be set. */
+ AdminClient_Error_ERROR_READING_FROM_SOCKET,
+
+ /** No data on socket. */
+ AdminClient_Error_SOCKET_NOT_READY,
+
+ /** Failed to deserialize response. */
+ AdminClient_Error_DESERIALIZATION_FAILED,
+
+ /** Failed to serialize request. */
+ AdminClient_Error_SERIALIZATION_FAILED,
+
+ /** Timeout waiting for response. */
+ AdminClient_Error_TIMEOUT,
+
+ /** Requested cookie and response did not contain cookie. */
+ AdminClient_Error_NO_COOKIE,
+};
+
+/** The biggest message that can be sent or received. */
+#define AdminClient_MAX_MESSAGE_SIZE 1024
+
+struct AdminClient_Result
+{
+ /** The error type of AdminClient_Error_NONE if there was no error. */
+ enum AdminClient_Error err;
+
+ /**
+ * When the request is made, this will hold the request bytes,
+ * after it will hold the response bytes. If there is an error
+ * during the sending of the request, it will still have the request bytes.
+ */
+ uint8_t messageBytes[AdminClient_MAX_MESSAGE_SIZE];
+
+ /** The deserialized response. */
+ Dict* responseDict;
+};
+
+struct AdminClient;
+
+char* AdminClient_errorString(enum AdminClient_Error err);
+
+
+struct AdminClient_Result* AdminClient_rpcCall(String* function,
+ Dict* args,
+ struct AdminClient* client,
+ struct Allocator* requestAlloc);
+
+
+struct AdminClient* AdminClient_new(struct sockaddr_storage* addr,
+ int addrLen,
+ String* adminPassword,
+ struct event_base* eventBase,
+ struct Log* logger,
+ struct Allocator* alloc);
+
+#endif
View
4 admin/CMakeLists.txt
@@ -12,8 +12,12 @@
add_library(cjdadmin
Admin.c
+ AdminClient.c
AuthorizedPasswords.c
Configurator.c
)
target_link_libraries(cjdadmin crypto cjdbenc_StandardBencSerializer)
+
+enable_testing()
+add_subdirectory(test)
View
239 admin/Configurator.c
@@ -12,188 +12,52 @@
* 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 "admin/AdminClient.h"
#include "admin/Configurator.h"
-#include "benc/serialization/BencSerializer.h"
-#include "benc/serialization/standard/StandardBencSerializer.h"
#include "benc/String.h"
#include "benc/Dict.h"
#include "benc/Int.h"
-#include "io/Writer.h"
-#include "io/ArrayWriter.h"
-#include "io/Reader.h"
-#include "io/ArrayReader.h"
#include "memory/Allocator.h"
-#include "memory/BufferAllocator.h"
-#include "util/Hex.h"
#include "util/Log.h"
-#include <unistd.h>
-#include <stdio.h>
-#include <string.h>
-#include <crypto_hash_sha256.h>
#include <event2/event.h>
-#include <errno.h>
-#define MAX_MESSAGE_SIZE 1024
struct Context
{
- evutil_socket_t socket;
- struct event* socketEvent;
- struct event_base* eventBase;
struct Log* logger;
- String* password;
-
- /** Means the next incoming message should be a cookie. */
- bool getCookie;
-
- /** The last cookie which was gotten from the server. */
- String* cookie;
+ struct Allocator* alloc;
+ struct AdminClient* client;
};
-static int calculateAuth(Dict* message,
- String* password,
- String* cookieStr,
- struct Allocator* alloc)
-{
- // Calculate the hash of the password.
- String* hashHex = String_newBinary(NULL, 64, alloc);
- uint8_t passAndCookie[64];
- uint32_t cookie = (cookieStr != NULL) ? strtoll(cookieStr->bytes, NULL, 10) : 0;
- snprintf((char*) passAndCookie, 64, "%s%u", password->bytes, cookie);
- uint8_t hash[32];
- crypto_hash_sha256(hash, passAndCookie, strlen((char*) passAndCookie));
- Hex_encode((uint8_t*)hashHex->bytes, 64, hash, 32);
-
- Dict_putString(message, String_new("hash", alloc), hashHex, alloc);
- Dict_putString(message, String_new("cookie", alloc), cookieStr, alloc);
-
- // serialize the message with the password hash
- uint8_t buffer[MAX_MESSAGE_SIZE];
- struct Writer* writer = ArrayWriter_new(buffer, MAX_MESSAGE_SIZE, alloc);
- if (StandardBencSerializer_get()->serializeDictionary(writer, message)) {
- return -1;
- }
- int length = writer->bytesWritten(writer);
-
- // calculate the hash of the message with the password hash
- crypto_hash_sha256(hash, buffer, length);
-
- // swap the hash of the message with the password hash into the location
- // where the password hash was.
- Hex_encode((uint8_t*)hashHex->bytes, 64, hash, 32);
- return 0;
-}
-
-static void timeout(evutil_socket_t socket, short eventType, void* vcontext)
-{
- struct Context* ctx = (struct Context*) vcontext;
- Log_critical(ctx->logger, "Timed out waiting for a response.");
- exit(-1);
-}
-
-static void sendMessage(Dict* message, struct Context* ctx)
+static void showMsg(struct AdminClient_Result* res, struct Context* ctx)
{
- if (!ctx->getCookie) {
- ctx->getCookie = true;
- Dict gc = Dict_CONST(String_CONST("q"), String_OBJ(String_CONST("cookie")), NULL);
- sendMessage(&gc, ctx);
- ctx->getCookie = false;
- }
-
- uint8_t memory[1024];
- struct Allocator* alloc = BufferAllocator_new(memory, 1024);
-
- uint8_t buff[MAX_MESSAGE_SIZE];
- struct Writer* writer = ArrayWriter_new(buff, MAX_MESSAGE_SIZE, alloc);
- if (StandardBencSerializer_get()->serializeDictionary(writer, message)) {
- Log_critical(ctx->logger, "Failed to serialize message");
- exit(-1);
- }
-
- if (!ctx->getCookie) {
- calculateAuth(message, ctx->password, ctx->cookie, alloc);
-
- writer = ArrayWriter_new(buff, MAX_MESSAGE_SIZE, alloc);
- if (StandardBencSerializer_get()->serializeDictionary(writer, message)) {
- Log_critical(ctx->logger, "Failed to serialize message after adding auth.");
- exit(-1);
- }
- }
-
- Log_keys1(ctx->logger, "Sending message: %s", buff);
- send(ctx->socket, buff, writer->bytesWritten(writer), 0);
-
- struct event* timeoutEvent = evtimer_new(ctx->eventBase, timeout, ctx);
- evtimer_add(timeoutEvent, (&(struct timeval) { .tv_sec = 5, .tv_usec = 0 }));
-
- event_base_dispatch(ctx->eventBase);
-
- evtimer_del(timeoutEvent);
+ Log_keys1(ctx->logger, "message bytes = [%s]", res->messageBytes);
+ #ifndef Log_KEYS
+ Log_critical(ctx->logger, "enable Log_LEVEL=KEYS to see message content.");
+ #endif
}
-static void incoming(evutil_socket_t socket, short eventType, void* vcontext)
+static void rpcCall(String* function, Dict* args, struct Context* ctx, struct Allocator* alloc)
{
- struct Context* ctx = (struct Context*) vcontext;
-
- uint8_t buff[MAX_MESSAGE_SIZE];
- ssize_t length = recv(socket, buff, MAX_MESSAGE_SIZE, 0);
- if (length == MAX_MESSAGE_SIZE) {
- Log_critical(ctx->logger, "Overlong resonse message.");
+ struct AdminClient_Result* res = AdminClient_rpcCall(function, args, ctx->client, alloc);
+ if (res->err) {
+ Log_critical2(ctx->logger,
+ "Failed to make function call [%s], error: [%s]",
+ AdminClient_errorString(res->err),
+ function->bytes);
+ showMsg(res, ctx);
exit(-1);
}
- if (length < 1) {
- if (length < 0) {
- Log_critical1(ctx->logger, "Error reading from socket, errno=%d", errno);
- } else {
- Log_critical(ctx->logger, "Socket not ready for reading.");
- }
+ String* error = Dict_getString(res->responseDict, String_CONST("error"));
+ if (error && !String_equals(error, String_CONST("none"))) {
+ Log_critical2(ctx->logger,
+ "Router responses with error: [%s]\nCalling function: [%s]",
+ error->bytes,
+ function->bytes);
+ showMsg(res, ctx);
exit(-1);
}
-
- uint8_t memory[MAX_MESSAGE_SIZE * 4];
- struct Allocator* alloc = BufferAllocator_new(memory, MAX_MESSAGE_SIZE * 4);
- struct Reader* reader = ArrayReader_new(buff, length, alloc);
- Dict message;
- if (StandardBencSerializer_get()->parseDictionary(reader, alloc, &message)) {
- Log_critical(ctx->logger, "Failed to deserialize response.");
- exit(-1);
- }
-
- if (ctx->getCookie) {
- ctx->getCookie = false;
- String* cookie = Dict_getString(&message, String_CONST("cookie"));
- if (!cookie) {
- Log_critical(ctx->logger, "Message did not contain cookie.");
- exit(-1);
- }
- ctx->cookie = cookie;
- } else {
- String* error = Dict_getString(&message, String_CONST("error"));
- if (!error) {
- Log_critical(ctx->logger, "Got no response.");
- exit(-1);
- }
- if (!String_equals(error, String_CONST("none"))) {
- Log_critical2(ctx->logger, "Got error response: %s\n"
- "Message content: %s", error->bytes, buff);
- exit(-1);
- }
- }
-
- event_base_loopexit(ctx->eventBase, NULL);
-}
-
-static void rpcCall(String* function, Dict* args, struct Context* ctx)
-{
- Dict a = (args) ? *args : NULL;
- Dict message = Dict_CONST(
- String_CONST("q"), String_OBJ(String_CONST("auth")), Dict_CONST(
- String_CONST("aq"), String_OBJ(function), Dict_CONST(
- String_CONST("args"), Dict_OBJ(&a), NULL
- )));
- sendMessage(&message, ctx);
}
static void authorizedPasswords(List* list, struct Context* ctx)
@@ -214,18 +78,20 @@ static void authorizedPasswords(List* list, struct Context* ctx)
}
Log_info(ctx->logger, "Flushing existing authorized passwords");
- rpcCall(String_CONST("AuthorizedPasswords_flush"), NULL, ctx);
+ rpcCall(String_CONST("AuthorizedPasswords_flush"), NULL, ctx, ctx->alloc);
for (uint32_t i = 0; i < count; i++) {
Dict* d = List_getDict(list, i);
String* passwd = Dict_getString(d, String_CONST("password"));
- Log_info1(ctx->logger, "Adding authorized password %d.", i);
+ Log_info1(ctx->logger, "Adding authorized password #[%d].", i);
Dict args = Dict_CONST(
String_CONST("authType"), Int_OBJ(1), Dict_CONST(
String_CONST("password"), String_OBJ(passwd), NULL
));
- rpcCall(String_CONST("AuthorizedPasswords_add"), &args, ctx);
+ struct Allocator* child = ctx->alloc->child(ctx->alloc);
+ rpcCall(String_CONST("AuthorizedPasswords_add"), &args, ctx, child);
+ child->free(child);
}
}
@@ -238,20 +104,18 @@ static void udpInterface(Dict* config, struct Context* ctx)
while (entry != NULL) {
String* key = (String*) entry->key;
if (entry->val->type != Object_DICT) {
- fprintf(stderr,
- "interfaces.UDPInterface.connectTo: entry [%s] is not a dictionary type.\n",
- key->bytes);
- abort();
+ Log_critical1(ctx->logger, "interfaces.UDPInterface.connectTo: entry [%s] "
+ "is not a dictionary type.", key->bytes);
+ exit(-1);
}
Dict* value = entry->val->as.dictionary;
Log_keys1(ctx->logger, "Attempting to connect to node [%s].", key->bytes);
- uint8_t buffer[256];
- struct Allocator* tmpAlloc = BufferAllocator_new(buffer, 256);
- Dict_putString(value, String_CONST("address"), key, tmpAlloc);
-
- rpcCall(String_CONST("UDPInterface_beginConnection"), value, ctx);
+ struct Allocator* perCallAlloc = ctx->alloc->child(ctx->alloc);
+ Dict_putString(value, String_CONST("address"), key, perCallAlloc);
+ rpcCall(String_CONST("UDPInterface_beginConnection"), value, ctx, perCallAlloc);
+ perCallAlloc->free(perCallAlloc);
entry = entry->next;
}
@@ -266,36 +130,19 @@ void Configurator_config(Dict* config,
struct Log* logger,
struct Allocator* alloc)
{
- struct Context context = {
- .eventBase = eventBase,
- .logger = logger,
- .password = adminPassword
- };
-
- context.socket = socket(addr->ss_family, SOCK_STREAM, 0);
- evutil_make_listen_socket_reuseable(context.socket);
- if (connect(context.socket, (struct sockaddr*)addr, addrLen)) {
- Log_critical1(logger, "Failed to connect, errno=%d", errno);
- exit(-1);
- }
-
- evutil_make_socket_nonblocking(context.socket);
-
- context.socketEvent =
- event_new(context.eventBase, context.socket, EV_READ | EV_PERSIST, incoming, &context);
- event_add(context.socketEvent, NULL);
+ struct Allocator* tempAlloc = alloc->child(alloc);
+ struct AdminClient* client =
+ AdminClient_new(addr, addrLen, adminPassword, eventBase, logger, tempAlloc);
+ struct Context ctx = { .logger = logger, .alloc = tempAlloc, .client = client };
+
List* authedPasswords = Dict_getList(config, String_CONST("authorizedPasswords"));
if (authedPasswords) {
- authorizedPasswords(authedPasswords, &context);
+ authorizedPasswords(authedPasswords, &ctx);
}
Dict* ifaces = Dict_getDict(config, String_CONST("interfaces"));
+ udpInterface(ifaces, &ctx);
- udpInterface(ifaces, &context);
-
- close(context.socket);
- event_del(context.socketEvent);
+ tempAlloc->free(tempAlloc);
}
-
-
View
81 admin/test/Admin_test.c
@@ -0,0 +1,81 @@
+/* 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 "admin/AdminClient.h"
+#include "benc/Dict.h"
+#include "benc/String.h"
+#include "benc/Int.h"
+#include "memory/Allocator.h"
+#include "memory/MallocAllocator.h"
+#include "io/FileWriter.h"
+#include "io/Writer.h"
+#include "util/Log.h"
+#include "exception/AbortHandler.h"
+
+#include <event2/event.h>
+#include <assert.h>
+
+struct Context {
+ struct Admin* admin;
+ bool called;
+};
+
+static void adminFunc(Dict* input, void* vcontext, String* txid)
+{
+ struct Context* ctx = vcontext;
+ ctx->called = true;
+ Dict d = Dict_CONST(String_CONST("called!"), Int_OBJ(1), NULL);
+ Admin_sendMessage(&d, txid, ctx->admin);
+}
+
+int main()
+{
+ struct Allocator* alloc = MallocAllocator_new(1<<20);
+
+ struct Writer* logwriter = FileWriter_new(stdout, alloc);
+ struct Log logger = { .writer = logwriter };
+
+ struct sockaddr_storage addr;
+ int addrLen = sizeof(struct sockaddr_storage);
+ memset(&addr, 0, sizeof(struct sockaddr_storage));
+
+ struct event_base* eventBase = event_base_new();
+
+ String* password = String_CONST("abcdefg12345");
+ struct Admin* admin =
+ Admin_new(&addr, addrLen, password, NULL, eventBase, AbortHandler_INSTANCE, &logger, alloc);
+
+ struct Context ctx = { .admin = admin, .called = false };
+
+ Admin_registerFunction("adminFunc", adminFunc, &ctx, true, NULL, admin);
+
+ struct sockaddr_storage* addrPtr;
+ String* retPassword;
+ Admin_getConnectInfo(&addrPtr, &addrLen, &retPassword, admin);
+
+ assert(String_equals(password, retPassword));
+
+ struct AdminClient* client =
+ AdminClient_new(addrPtr, addrLen, retPassword, eventBase, &logger, alloc);
+
+ assert(client);
+
+ struct AdminClient_Result* res =
+ AdminClient_rpcCall(String_CONST("adminFunc"), NULL, client, alloc);
+
+ assert(!res->err);
+ assert(Dict_getInt(res->responseDict, String_CONST("called!")));
+ assert(ctx.called);
+}
View
31 admin/test/CMakeLists.txt
@@ -0,0 +1,31 @@
+# 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/>.
+cmake_minimum_required(VERSION 2.4)
+
+string(REGEX REPLACE "^.*/" "" main_dir_name ${CMAKE_SOURCE_DIR})
+string(REPLACE ${CMAKE_SOURCE_DIR} ${main_dir_name} this_dir ${CMAKE_CURRENT_SOURCE_DIR})
+message("-- Tests to run for " ${this_dir})
+add_definitions(-g)
+
+
+# Anything in this dir which ends with "test.c" is considered a test.
+file(GLOB tests "*test.c")
+foreach(test_source_fullpath ${tests})
+ string(REGEX REPLACE "^.*/" "" test_source ${test_source_fullpath})
+ string(REPLACE "test.c" "test" test_bin ${test_source})
+ message(" " ${test_source})
+ add_executable(${test_bin} ${test_source})
+ target_link_libraries(${test_bin} cjdadmin)
+ add_test(${test_bin} ${test_bin})
+endforeach()
+# Add an empty line after tests.
+message("")
View
55 cjdroute.c
@@ -15,6 +15,7 @@
#include "admin/Admin.h"
#include "admin/AuthorizedPasswords.h"
#include "admin/Configurator.h"
+#include "benc/Int.h"
#include "crypto/AddressCalc.h"
#include "crypto/Crypto.h"
#include "crypto/CryptoAuth.h"
@@ -422,27 +423,17 @@ static void security(List* config, struct Log* logger, struct ExceptionHandler*
static void adminPing(Dict* input, void* vadmin, String* txid)
{
- uint8_t buffer[256];
- struct Allocator* alloc = BufferAllocator_new(buffer, 256);
-
- String* pong = String_CONST("pong");
- Dict* d = Dict_new(alloc);
- Dict_putString(d, CJDHTConstants_QUERY, pong, alloc);
-
- Admin_sendMessage(d, txid, (struct Admin*) vadmin);
+ Dict d = Dict_CONST(CJDHTConstants_QUERY, String_OBJ(String_CONST("pong")), NULL);
+ Admin_sendMessage(&d, txid, (struct Admin*) vadmin);
}
static void adminMemory(Dict* input, void* vcontext, String* txid)
{
struct Context* context = (struct Context*) vcontext;
- uint8_t buffer[256];
- struct Allocator* alloc = BufferAllocator_new(buffer, 256);
-
- String* bytes = String_CONST("bytes");
- Dict* d = Dict_new(alloc);
- Dict_putInt(d, bytes, MallocAllocator_bytesAllocated(context->allocator), alloc);
-
- Admin_sendMessage(d, txid, context->admin);
+ Dict d = Dict_CONST(
+ String_CONST("bytes"), Int_OBJ(MallocAllocator_bytesAllocated(context->allocator)), NULL
+ );
+ Admin_sendMessage(&d, txid, context->admin);
}
static void admin(Dict* mainConf, char* user, struct Log* logger, struct Context* context)
@@ -451,27 +442,21 @@ static void admin(Dict* mainConf, char* user, struct Log* logger, struct Context
String* address = Dict_getString(adminConf, String_CONST("bind"));
String* password = Dict_getString(adminConf, String_CONST("password"));
- if (!password || !address) {
- uint8_t randomPass[32];
- randomBase32(randomPass);
- Log_critical1(logger, "cjdns now requires you to specify an admin port and password.\n"
- "if you don't have an \"admin\" section in your configuration, "
- "add this.\n"
- "Otherwise add field so that it looks like this:\n"
- "\n"
- " \"admin\": {\n"
- " \"bind\": \"127.0.0.1:11234\",\n"
- " \"password\": \"%s\"\n"
- " }\n", randomPass);
- exit(-1);
- }
-
struct sockaddr_storage addr;
int addrLen = sizeof(struct sockaddr_storage);
- if (evutil_parse_sockaddr_port(address->bytes, (struct sockaddr*) &addr, &addrLen)) {
- Log_critical1(logger, "Unable to parse [%s] as an ip address port, "
- "eg: 127.0.0.1:11234", address->bytes);
- exit(-1);
+ memset(&addr, 0, sizeof(struct sockaddr_storage));
+ if (address) {
+ if (evutil_parse_sockaddr_port(address->bytes, (struct sockaddr*) &addr, &addrLen)) {
+ Log_critical1(logger, "Unable to parse [%s] as an ip address port, "
+ "eg: 127.0.0.1:11234", address->bytes);
+ exit(-1);
+ }
+ }
+
+ if (!password) {
+ uint8_t buff[32];
+ randomBase32(buff);
+ password = String_new((char*)buff, context->allocator);
}
context->admin = Admin_new(&addr,
Please sign in to comment.
Something went wrong with that request. Please try again.