From 459e2685158f1dc5a9cd3da1c3125889bc02f9d6 Mon Sep 17 00:00:00 2001 From: gayot_o Date: Tue, 19 Nov 2013 02:43:27 +0000 Subject: mplayer_server: first version of the server --- .gitignore | 2 ++ Makefile | 31 ++++++++++++++++ callbacks.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ escape.c | 35 ++++++++++++++++++ logs.c | 18 ++++++++++ logs.h | 9 +++++ main.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++ mplayer_server.h | 6 ++++ request.h | 14 ++++++++ 9 files changed, 315 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 callbacks.c create mode 100644 escape.c create mode 100644 logs.c create mode 100644 logs.h create mode 100644 main.c create mode 100644 mplayer_server.h create mode 100644 request.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..07a33ba --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +mplayer_server diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b8d27b8 --- /dev/null +++ b/Makefile @@ -0,0 +1,31 @@ +CC ?= gcc +CFLAGS += -W -Wall -std=gnu99 -Wextra +CFLAGS += -D _XOPEN_SOURCE +#CFLAGS += -D LOGS +NAME = mplayer_server +SRC = main.c callbacks.c escape.c logs.c + +all: depend $(NAME) + +depend: .depend + +.depend: $(SRC) + @$(RM) .depend + @$(CC) $(CFLAGS) -MM $^ > .depend + +include .depend + +OBJ = $(SRC:.c=.o) + +$(NAME): $(OBJ) + $(CC) -o $@ $^ $(LDFLAGS) + +clean: + $(RM) $(OBJ) + +fclean: clean + $(RM) $(NAME) + +re: fclean all + +.PHONY: all depend clean fclean all re diff --git a/callbacks.c b/callbacks.c new file mode 100644 index 0000000..e41fe8b --- /dev/null +++ b/callbacks.c @@ -0,0 +1,108 @@ +#include +#include + +#include "mplayer_server.h" +#include "request.h" + +#define CB(_name) int callback_##_name(const byte *buffer __attribute__((unused)), int size __attribute__((unused))) + +CB(load_url); +CB(pause); +CB(quit); +CB(snd_down); +CB(snd_up); +CB(fullscreen); +CB(mute); + +extern FILE *stream_g; + +typedef struct { + int opcode; + int (*cb)(const byte *, int); +} callback_t; + +static callback_t callbacks_g[] = { + {0, callback_load_url}, + {1, callback_pause}, + {2, callback_quit}, + {3, callback_snd_up}, + {4, callback_snd_down}, + {5, callback_fullscreen}, + {6, callback_mute}, +}; + +/* + * returns a new malloced() null terminated escaped string + * if the string is considered dangerous, then the function returns NULL + */ + +char *real_escape_string(const byte *buf, int size); + +void *get_assoc_cb(int opcode) +{ + for (size_t i = 0; i < sizeof(callbacks_g) / sizeof(callbacks_g[0]); ++i) { + if (callbacks_g[i].opcode == opcode) { + return callbacks_g[i].cb; + } + } + + return NULL; +} + +#define SEND_CMD(_fmt, ...) \ + do { \ + _log(_fmt, ##__VA_ARGS__); \ + fprintf(stream_g, _fmt "\n", ##__VA_ARGS__); \ + } while (0) + +/* TODO refuse the execution of commands with arguments when they do not need */ +CB(load_url) +{ + /* XXX beware, someone may perform arbitrary code injection */ + char *escaped = real_escape_string(buffer, size); + + if (escaped == NULL) { + return -1; + } + + SEND_CMD("loadfile '%s'", escaped); + + free(escaped); + return 0; +} + +CB(pause) +{ + SEND_CMD("pause"); + return 0; +} + +CB(quit) +{ + SEND_CMD("quit"); + return 0; +} + +CB(snd_down) +{ + SEND_CMD("volume -5"); + return 0; +} + +CB(snd_up) +{ + SEND_CMD("volume +5"); + return 0; +} + +CB(fullscreen) +{ + SEND_CMD("vo_fullscreen"); + return 0; +} + +CB(mute) +{ + SEND_CMD("mute"); + return 0; +} diff --git a/escape.c b/escape.c new file mode 100644 index 0000000..be801e2 --- /dev/null +++ b/escape.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +#include "request.h" + +char *real_escape_string(const byte *src, int size) +{ + char *escaped; + char *pos; + + if (memchr(src, '\0', size - 1) || memchr(src, '\n', size) || memchr(src, '\r', size)) { + fprintf(stderr, "cannot handle this string\n"); + return NULL; + } + + pos = escaped = malloc(sizeof(char) * (size * 2 + 1)); + + if (escaped == NULL) { + fprintf(stderr, "malloc failed: %m\n"); + return NULL; + } + + for (int i = 0; i < size; ++i) { + if (!isalnum(src[i])) { + *escaped++ = '\\'; + } + *escaped++ = src[i]; + } + + *escaped = '\0'; + + return pos; +} diff --git a/logs.c b/logs.c new file mode 100644 index 0000000..01d93fc --- /dev/null +++ b/logs.c @@ -0,0 +1,18 @@ +#include +#include + +void _log(const char *fmt, ...) +{ +#ifdef LOGS + va_list ap; + + va_start(ap, fmt); + + vfprintf(stderr, fmt, ap); + fputc('\n', stderr); + + va_end(ap); +#else + (void) fmt; +#endif +} diff --git a/logs.h b/logs.h new file mode 100644 index 0000000..f36695d --- /dev/null +++ b/logs.h @@ -0,0 +1,9 @@ +#ifndef LOGS_H +#define LOGS_H + +#include + +__attribute__((format(printf, 1, 2))) +void _log(const char *fmt, ...); + +#endif /* LOGS_H */ diff --git a/main.c b/main.c new file mode 100644 index 0000000..bb74011 --- /dev/null +++ b/main.c @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "mplayer_server.h" +#include "request.h" + +FILE *stream_g; + +static request_t buffer_g; + +void *get_assoc_cb(int opcode); + +/* returns a socket listing to port or -1 if something failed */ +static int bind_socket(uint16_t port) +{ + int sock = socket(AF_INET, SOCK_STREAM, 0); + struct sockaddr_in sin; + + if (sock < 0) { + perror("socket"); + return -1; + } + + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (int []){1}, sizeof(int)); + + if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) { + perror("bind"); + return -1; + } + + if (listen(sock, 10) < 0) { + perror("listen"); + return -1; + } + + return sock; +} + +/* TODO allow multiple clients to send queries */ +static int event_loop(int sock) +{ + for (;;) { + int csock = accept(sock, NULL, NULL); + + int size = read(csock, &buffer_g, sizeof(buffer_g)); + + _log("received: [%.*s]\n", size - (int)sizeof(buffer_g.opcode), buffer_g.data); + + if (size >= (int)sizeof(buffer_g.opcode)) { + int (*cb)(const byte *, int) = get_assoc_cb(buffer_g.opcode); + + if (cb != NULL) { + (*cb)(buffer_g.data, size - (int)sizeof(buffer_g.opcode)); + } + } + + close(csock); + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + stream_g = popen("/usr/bin/mplayer -quiet -slave -idle", "w"); + + if (stream_g == NULL) { + fprintf(stderr, "cannot run mplayer: %m\n"); + return -1; + } + + setvbuf(stream_g, NULL, _IONBF, 0); + + int sock = bind_socket((argc < 2) ? 4333 : atoi(argv[1])); + + if (sock >= 0) { + signal(SIGPIPE, SIG_IGN); + + event_loop(sock); + } + + pclose(stream_g); + return 0; +} diff --git a/mplayer_server.h b/mplayer_server.h new file mode 100644 index 0000000..c3046da --- /dev/null +++ b/mplayer_server.h @@ -0,0 +1,6 @@ +#ifndef MPLAYER_SERVER_H +#define MPLAYER_SERVER_H + +#include "logs.h" + +#endif /* MPLAYER_SERVER_H */ diff --git a/request.h b/request.h new file mode 100644 index 0000000..6c5925a --- /dev/null +++ b/request.h @@ -0,0 +1,14 @@ +#ifndef REQUEST_H +#define REQUEST_H + +#include + +typedef uint8_t byte; + +typedef struct { + byte opcode; + + byte data[512]; +} request_t; + +#endif /* REQUEST_H */ -- cgit v1.2.3