416 lines
13 KiB
C
416 lines
13 KiB
C
/*
|
|
Copyright (c) 2013-2015 pCloud Ltd. All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met: Redistributions of source code must retain the above
|
|
copyright notice, this list of conditions and the following
|
|
disclaimer. Redistributions in binary form must reproduce the above
|
|
copyright notice, this list of conditions and the following
|
|
disclaimer in the documentation and/or other materials provided with
|
|
the distribution. Neither the name of pCloud Ltd nor the names of
|
|
its contributors may be used to endorse or promote products derived
|
|
from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL pCloud
|
|
Ltd BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
|
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
|
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
|
DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
poverlay is the "overlay server" (contrast with overlay_client). It
|
|
listens for request messages generated by overlay_client on a unix
|
|
socket, invokes the appropriate callback based on the message type,
|
|
and generates a response message. It then writes the response
|
|
message back to the socket where the overlay_client will read and
|
|
act upon it.
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <time.h>
|
|
|
|
#include "pcompat.h"
|
|
#include "poverlay_protocol.h"
|
|
|
|
#include "poverlay.h"
|
|
#include "ppathstatus.h"
|
|
|
|
#include "plibs.h"
|
|
|
|
#define POVERLAY_BUFSIZE 512
|
|
|
|
int overlays_running = 1;
|
|
int callbacks_running = 1;
|
|
|
|
// Serialization function
|
|
size_t serialize_response_message(const response_message *resp,
|
|
char **out_buffer) {
|
|
// Calculate total size needed
|
|
size_t msg_size = sizeof(uint32_t) + sizeof(uint64_t) + resp->msg->length;
|
|
size_t total_size =
|
|
sizeof(size_t) + msg_size + sizeof(size_t) + resp->payloadsz;
|
|
|
|
// Allocate buffer
|
|
*out_buffer = (char *)malloc(total_size);
|
|
if (*out_buffer == NULL)
|
|
return 0;
|
|
|
|
char *ptr = *out_buffer;
|
|
|
|
// Serialize msg size
|
|
*(size_t *)ptr = htobe64(msg_size);
|
|
ptr += sizeof(size_t);
|
|
|
|
// Serialize msg
|
|
*(uint32_t *)ptr = htonl(resp->msg->type);
|
|
ptr += sizeof(uint32_t);
|
|
|
|
*(uint64_t *)ptr = htobe64(resp->msg->length);
|
|
ptr += sizeof(uint64_t);
|
|
|
|
memcpy(ptr, resp->msg->value, resp->msg->length);
|
|
ptr += resp->msg->length;
|
|
|
|
// Serialize payload size
|
|
*(size_t *)ptr = htobe64(resp->payloadsz);
|
|
ptr += sizeof(size_t);
|
|
|
|
// Serialize payload
|
|
if (resp->payloadsz > 0 && resp->payload != NULL) {
|
|
memcpy(ptr, resp->payload, resp->payloadsz);
|
|
}
|
|
|
|
return total_size;
|
|
}
|
|
|
|
void psync_overlay_main_loop() {
|
|
struct sockaddr_un addr;
|
|
int fd, cl;
|
|
|
|
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
|
|
debug(D_ERROR, "Unix socket error failed to open %s", POVERLAY_SOCK_PATH);
|
|
return;
|
|
}
|
|
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sun_family = AF_UNIX;
|
|
strncpy(addr.sun_path, POVERLAY_SOCK_PATH, sizeof(addr.sun_path) - 1);
|
|
|
|
unlink(POVERLAY_SOCK_PATH);
|
|
|
|
if (bind(fd, (struct sockaddr *)&addr,
|
|
strlen(POVERLAY_SOCK_PATH) + sizeof(addr.sun_family)) == -1) {
|
|
debug(D_ERROR, "Unix socket bind error");
|
|
return;
|
|
}
|
|
|
|
if (listen(fd, 5) == -1) {
|
|
debug(D_ERROR, "Unix socket listen error");
|
|
return;
|
|
}
|
|
|
|
while (1) {
|
|
if ((cl = accept(fd, NULL, NULL)) == -1) {
|
|
debug(D_ERROR, "Unix socket accept error");
|
|
continue;
|
|
}
|
|
|
|
// handle the request in a new thread
|
|
psync_run_thread1("Pipe request handle routine",
|
|
psync_overlay_handle_request, // thread proc
|
|
(LPVOID)&cl // thread parameter
|
|
);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void psync_overlay_handle_request(void *lpvParam) {
|
|
int *sockfd; // pcloud socket file descriptor
|
|
int rc; // bytes read / written per iteration
|
|
int readbytes; // total bytes read from request
|
|
char rqbuf[POVERLAY_BUFSIZE]; // request buffer, contains the request message
|
|
char *rqbufp; // request buffer ptr, for convenient iteration
|
|
request_message *request; // request message
|
|
response_message *response; // response message and payload
|
|
|
|
request = NULL;
|
|
response = NULL;
|
|
readbytes = 0;
|
|
rqbufp = &rqbuf[0];
|
|
sockfd = (int *)lpvParam;
|
|
|
|
// read the request from the socket into the request buffer
|
|
memset(rqbuf, 0, POVERLAY_BUFSIZE);
|
|
while ((rc = read(*sockfd, rqbufp, (POVERLAY_BUFSIZE - readbytes))) > 0) {
|
|
readbytes += rc;
|
|
rqbufp = rqbufp + rc;
|
|
if (readbytes > 12) {
|
|
request = (request_message *)rqbuf;
|
|
if (request->length == (uint64_t)readbytes)
|
|
break;
|
|
}
|
|
}
|
|
if (rc == -1) {
|
|
debug(D_ERROR, "Unix socket read error");
|
|
goto cleanup;
|
|
} else if (rc == 0 && readbytes == 0) {
|
|
debug(D_NOTICE, "Connection closed by client before sending data");
|
|
goto cleanup;
|
|
}
|
|
|
|
// allocate and initialize response message
|
|
response = (response_message *)psync_malloc(sizeof(response_message));
|
|
memset(response, 0, sizeof(response_message));
|
|
response->msg = NULL;
|
|
response->payload = NULL;
|
|
response->payloadsz = 0;
|
|
|
|
// get the response for the request, and write the response to the
|
|
// sockfd.
|
|
request = (request_message *)rqbuf;
|
|
if (request) {
|
|
psync_overlay_get_response(request, response);
|
|
|
|
char *rsbufp;
|
|
size_t bytes_written = 0;
|
|
size_t responsesz = serialize_response_message(response, &rsbufp);
|
|
if (responsesz <= 0) {
|
|
debug(D_ERROR, "failed to serialize response message");
|
|
return;
|
|
}
|
|
|
|
while (bytes_written < responsesz) {
|
|
ssize_t written =
|
|
write(*sockfd, rsbufp + bytes_written, responsesz - bytes_written);
|
|
if (written == -1) {
|
|
if (errno == EINTR) {
|
|
// Interrupted by signal, try again
|
|
continue;
|
|
}
|
|
debug(D_ERROR, "failed to write to socket: %s", strerror(errno));
|
|
break;
|
|
}
|
|
bytes_written += written;
|
|
}
|
|
free(rsbufp);
|
|
if (bytes_written < responsesz) {
|
|
debug(D_ERROR, "failed to write entire message to socket");
|
|
return;
|
|
}
|
|
|
|
debug(D_NOTICE,
|
|
"Successfully sent full reply: %zu bytes structure, %zu bytes "
|
|
"additional data",
|
|
response->msg->length, response->payloadsz);
|
|
} else {
|
|
debug(D_ERROR, "No valid request received");
|
|
}
|
|
|
|
cleanup:
|
|
if (sockfd) {
|
|
close(*sockfd);
|
|
}
|
|
if (response) {
|
|
if (response->msg) {
|
|
psync_free(response->msg);
|
|
}
|
|
if (response->payload) {
|
|
psync_free(response->payload);
|
|
}
|
|
psync_free(response);
|
|
}
|
|
|
|
debug(D_NOTICE, "InstanceThread exiting.");
|
|
}
|
|
|
|
poverlay_callback *callbacks;
|
|
static int callbacks_size = 15;
|
|
static const int calbacks_lower_band = 20;
|
|
|
|
// registers an overlay callback for a given message type.
|
|
int psync_overlay_register_callback(int msgtype, poverlay_callback callback) {
|
|
poverlay_callback *callbacks_old = callbacks;
|
|
int callbacks_size_old = callbacks_size;
|
|
if (msgtype < calbacks_lower_band)
|
|
return -1;
|
|
if (msgtype > (calbacks_lower_band + callbacks_size)) {
|
|
callbacks_size = msgtype - calbacks_lower_band + 1;
|
|
psync_overlay_init_callbacks();
|
|
memcpy(callbacks, callbacks_old,
|
|
callbacks_size_old * sizeof(poverlay_callback));
|
|
psync_free(callbacks_old);
|
|
}
|
|
callbacks[msgtype - calbacks_lower_band] = callback;
|
|
return 0;
|
|
}
|
|
|
|
void psync_overlay_init_callbacks() {
|
|
callbacks = (poverlay_callback *)psync_malloc(sizeof(poverlay_callback) *
|
|
callbacks_size);
|
|
memset(callbacks, 0, sizeof(poverlay_callback) * callbacks_size);
|
|
}
|
|
|
|
static void psync_overlay_get_status_response(request_message *request,
|
|
response_message *response,
|
|
size_t available_space) {
|
|
psync_path_status_t stat;
|
|
|
|
stat = PSYNC_PATH_STATUS_NOT_OURS;
|
|
|
|
if (overlays_running) {
|
|
stat = psync_path_status_get(request->value);
|
|
}
|
|
switch (psync_path_status_get_status(stat)) {
|
|
case PSYNC_PATH_STATUS_IN_SYNC:
|
|
response->msg->type = 10;
|
|
break;
|
|
case PSYNC_PATH_STATUS_IN_PROG:
|
|
response->msg->type = 12;
|
|
break;
|
|
case PSYNC_PATH_STATUS_PAUSED:
|
|
case PSYNC_PATH_STATUS_REMOTE_FULL:
|
|
case PSYNC_PATH_STATUS_LOCAL_FULL:
|
|
response->msg->type = 11;
|
|
break;
|
|
default:
|
|
response->msg->type = 13;
|
|
snprintf(response->msg->value, available_space, "No.");
|
|
}
|
|
}
|
|
|
|
static void
|
|
psync_overlay_get_overlay_response_payload(request_message *request,
|
|
response_message *response) {
|
|
if (!response->payload) {
|
|
response->payloadsz = 0;
|
|
debug(D_NOTICE, "Callback succeeded with no reply data");
|
|
return;
|
|
}
|
|
|
|
if (request->type == 23) {
|
|
// LISTSYNC
|
|
psync_folder_list_t *folders = (psync_folder_list_t *)response->payload;
|
|
response->payloadsz = sizeof(psync_folder_list_t) +
|
|
folders->foldercnt * sizeof(psync_folder_t);
|
|
} else if (request->type == 24) {
|
|
// ADDSYNC
|
|
response->payloadsz = sizeof(psync_syncid_t);
|
|
} else {
|
|
// default: response has no payload
|
|
response->payloadsz = 0;
|
|
}
|
|
|
|
if (response->payload == NULL) {
|
|
response->payloadsz = 0;
|
|
debug(D_NOTICE, "No reply data received");
|
|
return;
|
|
}
|
|
|
|
debug(D_NOTICE, "Callback succeeded with reply data, length: %zu",
|
|
response->payloadsz);
|
|
}
|
|
|
|
static void psync_overlay_get_overlay_response(request_message *request,
|
|
response_message *response,
|
|
size_t available_space) {
|
|
int cbidx; // callback index (based on message type)
|
|
int cbret; // callback return value
|
|
|
|
cbidx = request->type - 20;
|
|
cbret = 0;
|
|
|
|
if (!callbacks_running || (request->type >= ((uint32_t)calbacks_lower_band +
|
|
(uint32_t)callbacks_size))) {
|
|
response->msg->type = 13;
|
|
snprintf(response->msg->value, available_space, "Invalid type.");
|
|
debug(D_NOTICE, "Invalid request type: %u", request->type);
|
|
return;
|
|
}
|
|
|
|
if (!callbacks[cbidx]) {
|
|
response->msg->type = 13;
|
|
snprintf(response->msg->value, available_space,
|
|
"No callback with this id registered.");
|
|
debug(D_NOTICE, "No callback registered for type: %u", request->type);
|
|
return;
|
|
}
|
|
|
|
cbret = callbacks[cbidx](request->value, &response->payload);
|
|
if (cbret == 0) {
|
|
response->msg->type = 0;
|
|
psync_overlay_get_overlay_response_payload(request, response);
|
|
} else {
|
|
response->msg->type = cbret;
|
|
snprintf(response->msg->value, available_space,
|
|
"Callback returned error code.");
|
|
debug(D_NOTICE, "Callback failed with return code: %d", cbret);
|
|
}
|
|
}
|
|
|
|
void psync_overlay_get_response(request_message *request,
|
|
response_message *response) {
|
|
|
|
const char *dbgmsg; // debug messages
|
|
size_t value_avail; // space available to store value (flexible array)
|
|
|
|
dbgmsg = NULL;
|
|
response->msg = (message *)psync_malloc(POVERLAY_BUFSIZE);
|
|
memset(response->msg, 0, POVERLAY_BUFSIZE);
|
|
response->msg->length = 0;
|
|
response->payload = NULL;
|
|
response->payloadsz = 0;
|
|
value_avail = POVERLAY_BUFSIZE - sizeof(message);
|
|
|
|
// never print the crypto password to the logs in plain text
|
|
dbgmsg = (request->type == 20) ? "REDACTED" : request->value;
|
|
debug(D_NOTICE, "Client Request type [%u] len [%lu] string: [%s]",
|
|
request->type, request->length, dbgmsg);
|
|
|
|
if (request->type < 20) {
|
|
psync_overlay_get_status_response(request, response, value_avail);
|
|
} else {
|
|
psync_overlay_get_overlay_response(request, response, value_avail);
|
|
}
|
|
|
|
// a message with a type != 13 and a null string value after
|
|
// processing is considered successful. set value to "Ok."
|
|
if (response->msg->type != 13 && response->msg->value[0] == '\0') {
|
|
snprintf(response->msg->value, value_avail, "Ok.");
|
|
} else {
|
|
debug(D_WARNING,
|
|
"not updating value to Ok: response->msg->type=%d, "
|
|
"response->msg->value=%s",
|
|
response->msg->type, response->msg->value);
|
|
}
|
|
|
|
// truncate messages that exceed the buffer boundaries
|
|
size_t value_length = strnlen(response->msg->value, value_avail);
|
|
response->msg->length = sizeof(message) + value_length + 1;
|
|
if (response->msg->length > POVERLAY_BUFSIZE) {
|
|
response->msg->length = POVERLAY_BUFSIZE;
|
|
response->msg->value[value_avail - 1] = '\0';
|
|
debug(D_WARNING, "Response message truncated to fit buffer");
|
|
}
|
|
}
|
|
|
|
void psync_overlay_stop_overlays() { overlays_running = 0; }
|
|
void psync_overlay_start_overlays() { overlays_running = 1; }
|
|
void psync_overlay_stop_overlay_callbacks() { callbacks_running = 0; }
|
|
void psync_overlay_start_overlay_callbacks() { callbacks_running = 1; }
|
|
int psync_overlay_overlays_running() { return overlays_running; }
|
|
int psync_overlay_callbacks_running() { return callbacks_running; }
|