blob: b4fa16bfeca7a89aa212358c84aab0f3a1fefe37 [file] [log] [blame] [raw]
/*
* Copyright 2015-2018 Rivoreo
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
/* This part is actually the API server; it is named 'client' because it is
* the client of the local daemon sshoutd(8) */
#include "common.h"
#include "client.h"
#include "api.h"
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <arpa/inet.h>
static void send_api_pass(int version) {
uint32_t length = 1 + 6 + 2;
struct sshout_api_packet *packet = malloc(4 + length);
if(!packet) {
syslog(LOG_ERR, "send_api_pass: out of memory");
exit(1);
}
packet->length = htonl(length);
packet->type = htons(SSHOUT_API_PASS);
memcpy(packet->data, "SSHOUT", 6);
*(uint16_t *)(packet->data + 6) = htons(version);
while(write(STDOUT_FILENO, packet, 4 + length) < 0) {
if(errno == EINTR) continue;
syslog(LOG_ERR, "send_api_pass: write: STDOUT_FILENO: errno %d", errno);
exit(1);
}
free(packet);
}
static void send_api_error(int code, const char *message) {
}
static void send_api_message(struct local_message *local_message) {
}
static void send_api_online_users(struct local_online_users_info *local_info) {
uint32_t length = 1 + 2 + 2;
struct sshout_api_packet *packet = malloc(4 + length);
if(!packet) {
syslog(LOG_ERR, "send_api_online_users: out of memory");
//return;
exit(1);
}
packet->type = htons(SSHOUT_API_ONLINE_USERS_INFO);
uint8_t *p = packet->data;
*(uint16_t *)p = htons(local_info->your_id);
p += 2;
*(uint16_t *)p = htons(local_info->count);
p += 2;
int i = 0;
while(i < local_info->count) {
const struct local_online_user *u = local_info->user + i++;
uint8_t user_name_len = strnlen(u->user_name, USER_NAME_MAX_LENGTH);
uint8_t host_name_len = strnlen(u->host_name, HOST_NAME_MAX_LENGTH);
length += 2 + 1 + user_name_len + 1 + host_name_len;
packet = realloc(packet, 4 + length);
if(!packet) {
syslog(LOG_ERR, "send_api_online_users: out of memory");
exit(1);
}
*(uint16_t *)p = htons(u->id);
p += 2;
*(uint8_t *)p = user_name_len;
p += 4;
memcpy(p, u->user_name, user_name_len);
p += user_name_len;
*(uint8_t *)p = host_name_len;
p += 4;
memcpy(p, u->host_name, host_name_len);
p += host_name_len;
}
packet->length = htonl(length);
while(write(STDOUT_FILENO, packet, 4 + length) < 0) {
if(errno == EINTR) continue;
//r = -1;
//break;
syslog(LOG_ERR, "send_api_online_users: write: STDOUT_FILENO: errno %d", errno);
exit(1);
}
free(packet);
}
static void send_api_user_state(const char *user, int online) {
}
static int api_version = 0;
static char *syslog_ident;
static void client_api_init_io(const char *user_name) {
//char ident[8 + USER_NAME_MAX_LENGTH + 4 + 1];
size_t len = 8 + USER_NAME_MAX_LENGTH + 4 + 1;
syslog_ident = malloc(len);
if(!syslog_ident) {
perror("malloc");
exit(1);
}
snprintf(syslog_ident, len, "sshoutd:%s:api", user_name);
openlog(syslog_ident, LOG_PID, LOG_DAEMON);
syslog(LOG_INFO, "API server started");
}
static void client_api_do_local_packet(int fd) {
if(!api_version) {
// XXX
sleep(1);
return;
}
struct local_packet *packet;
int e = get_local_packet(fd, &packet);
switch(e) {
case GET_PACKET_EOF:
send_api_error(SSHOUT_API_ERROR_SERVER_CLOSED, "Server closed connection");
close(fd);
exit(0);
case GET_PACKET_ERROR:
send_api_error(SSHOUT_API_ERROR_LOCAL_PACKET_CORRUPT, strerror(errno));
close(fd);
exit(1);
case GET_PACKET_SHORT_READ:
send_api_error(SSHOUT_API_ERROR_LOCAL_PACKET_CORRUPT, "Local packet short read");
close(fd);
exit(1);
case GET_PACKET_TOO_LARGE:
send_api_error(SSHOUT_API_ERROR_LOCAL_PACKET_TOO_LARGE, "Received local packet too large");
close(fd);
exit(1);
case GET_PACKET_OUT_OF_MEMORY:
send_api_error(SSHOUT_API_ERROR_OUT_OF_MEMORY, "Out of memory");
close(fd);
exit(1);
case 0:
break;
default:
send_api_error(SSHOUT_API_ERROR_INTERNAL_ERROR, "Internal error");
syslog(LOG_ERR, "Unknown error %d from get_local_packet", e);
abort();
}
switch(packet->type) {
case SSHOUT_LOCAL_DISPATCH_MESSAGE:
send_api_message((struct local_message *)packet->data);
break;
case SSHOUT_LOCAL_ONLINE_USERS_INFO:
send_api_online_users((struct local_online_users_info *)packet->data);
break;
case SSHOUT_LOCAL_USER_ONLINE:
case SSHOUT_LOCAL_USER_OFFLINE:
send_api_user_state((char *)packet->data, packet->type == SSHOUT_LOCAL_USER_ONLINE);
break;
case SSHOUT_LOCAL_USER_NOT_FOUND:
send_api_error(SSHOUT_API_ERROR_USER_NOT_FOUND, (char *)packet->data);
break;
default:
syslog(LOG_WARNING, "Unknown local packet type %d", packet->type);
break;
}
free(packet);
}
static void client_api_do_stdin(int fd) {
struct sshout_api_packet *packet;
uint32_t length;
int e = get_api_packet(STDIN_FILENO, &packet, &length);
switch(e) {
case GET_PACKET_EOF:
close(fd);
exit(0);
case GET_PACKET_ERROR:
syslog(LOG_ERR, "STDIN_FILENO: %s", strerror(errno));
close(fd);
exit(1);
case GET_PACKET_SHORT_READ:
syslog(LOG_ERR, "STDIN_FILENO short read");
close(fd);
exit(1);
case GET_PACKET_TOO_SMALL:
syslog(LOG_ERR, "Received API packet too small");
close(fd);
exit(1);
case GET_PACKET_TOO_LARGE:
syslog(LOG_ERR, "Received API packet too large (%u bytes)", length);
close(fd);
exit(1);
case GET_PACKET_OUT_OF_MEMORY:
syslog(LOG_ERR, "Out of memory");
close(fd);
exit(1);
case 0:
break;
default:
syslog(LOG_ERR, "Unknown error %d from get_api_packet", e);
abort();
}
// packet->type have only 1 byte, doesn't need to convert byte order
if(!api_version && packet->type != SSHOUT_API_HELLO) {
syslog(LOG_ERR, "Received API packet type %hhu before handshake", ntohs(packet->type));
close(fd);
exit(1);
}
switch(packet->type) {
case SSHOUT_API_HELLO:
if(memcmp(packet->data, "SSHOUT", 6)) {
syslog(LOG_ERR, "SSHOUT_API_HELLO: handshake failed, magic isn't match");
close(fd);
exit(1);
}
api_version = ntohs(*(uint16_t *)(packet->data + 6));
if(api_version != 1) {
syslog(LOG_ERR, "SSHOUT_API_HELLO: handshake failed, unsupported API version %hu",
(unsigned short int)api_version);
close(fd);
exit(1);
}
send_api_pass(1);
break;
case SSHOUT_API_GET_ONLINE_USER:
if(client_send_request_get_online_users(fd) < 0) {
syslog(LOG_ERR,
"SSHOUT_API_GET_ONLINE_USER: client_send_request_get_online_users failed, errno %d",
errno);
break;
}
break;
case SSHOUT_API_SEND_MESSAGE:
break;
default:
syslog(LOG_ERR, "Received unknown API packet type %hhu", ntohs(packet->type));
break;
}
free(packet);
}
void client_api_get_actions(struct client_backend_actions *a) {
a->init_io = client_api_init_io;
a->do_local_packet = client_api_do_local_packet;
a->do_stdin = client_api_do_stdin;
a->do_after_signal = NULL;
a->do_tick = NULL;
}