diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 31 | ||||
-rw-r--r-- | callbacks.c | 108 | ||||
-rw-r--r-- | escape.c | 35 | ||||
-rw-r--r-- | logs.c | 18 | ||||
-rw-r--r-- | logs.h | 9 | ||||
-rw-r--r-- | main.c | 92 | ||||
-rw-r--r-- | mplayer_server.h | 6 | ||||
-rw-r--r-- | request.h | 14 |
9 files changed, 315 insertions, 0 deletions
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 <stdio.h> +#include <stdlib.h> + +#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 <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> + +#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; +} @@ -0,0 +1,18 @@ +#include <stdarg.h> +#include <stdio.h> + +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 +} @@ -0,0 +1,9 @@ +#ifndef LOGS_H +#define LOGS_H + +#include <stdarg.h> + +__attribute__((format(printf, 1, 2))) +void _log(const char *fmt, ...); + +#endif /* LOGS_H */ @@ -0,0 +1,92 @@ +#include <signal.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> + +#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 <stdint.h> + +typedef uint8_t byte; + +typedef struct { + byte opcode; + + byte data[512]; +} request_t; + +#endif /* REQUEST_H */ |