commit 99cd88f50ee31bd859b9baa4e60e872e6ad9e213
Author: Nihal Jere <nihal@nihaljere.xyz>
Date: Sun, 25 Apr 2021 01:09:29 -0500
initial commit
Diffstat:
A | Makefile | | | 22 | ++++++++++++++++++++++ |
A | atc.c | | | 70 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | atd.c | | | 281 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | atd.h | | | 18 | ++++++++++++++++++ |
A | atsim.c | | | 122 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | util.c | | | 72 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | util.h | | | 20 | ++++++++++++++++++++ |
7 files changed, 605 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -0,0 +1,22 @@
+CC ?= gcc
+CFLAGS =
+
+SRC = atd.c atc.c atsim.c util.c
+OBJ = $(SRC:.c=.o)
+
+all: atd atc atsim
+
+atd: atd.o util.o
+ $(CC) $(CFLAGS) atd.o util.o -o atd
+
+atc: atc.o
+ $(CC) $(CFLAGS) atc.o -o atc
+
+atsim: atsim.o
+ $(CC) $(CFLAGS) atsim.o -o atsim
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ rm -f $(OBJ) atd atc atsim
diff --git a/atc.c b/atc.c
@@ -0,0 +1,70 @@
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include "atd.h"
+
+int
+call(int fd, char *num)
+{
+ char callcode = CMD_DIAL;
+ int ret, left = strlen(num);
+ do {
+ ret = write(fd, &callcode, 1);
+ if (ret == -1)
+ return -1;
+ } while (ret);
+
+ do {
+ ret = write(fd, num, left);
+ if (ret == -1)
+ return -1;
+ num += ret;
+ left -= ret;
+ } while (left);
+
+ return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+ enum ops cmd;
+ struct sockaddr_un addr = {
+ .sun_family = AF_UNIX,
+ .sun_path = "/tmp/atd-socket"
+ };
+ if (argc < 2)
+ return 1;
+
+ if (strcmp(argv[1], "call") == 0) {
+ if (argc < 3)
+ return 1;
+
+ cmd = CMD_DIAL;
+ } else if (strcmp(argv[1], "answer") == 0) {
+ cmd = CMD_ANSWER;
+ } else if (strcmp(argv[1], "hangup") == 0) {
+ cmd = CMD_HANGUP;
+ }
+
+ int sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock == -1) {
+ fprintf(stderr, "failed to open socket\n");
+ return 1;
+ }
+
+ int con = connect(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
+ if (con == -1) {
+ fprintf(stderr, "failed to connect\n");
+ close(sock);
+ return 1;
+ }
+
+ close(con);
+ close(sock);
+
+ return 0;
+}
diff --git a/atd.c b/atd.c
@@ -0,0 +1,281 @@
+#include <poll.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "atd.h"
+#include "util.h"
+
+#define AT_MAX 256
+#define ATD_SOCKET "/tmp/atd-socket"
+
+#define QUEUE_MAX 100
+#define STDOUT 0
+#define STDIN 1
+#define STDERR 2
+#define LISTENER 3
+#define BACKEND 5
+#define SIGNALINT 6
+#define RSRVD_FDS 6
+#define MAX_FDS 16
+
+#define BUFSIZE 256
+
+char *argv0;
+
+struct {
+ char *ptrs[QUEUE_MAX];
+ char **start;
+ char **next;
+ size_t count;
+} atq;
+
+struct fdbuf {
+ ssize_t len;
+ char buf[BUFSIZE];
+ char *ptr;
+};
+
+int at_enqueue(char *cmd)
+{
+ if (atq.count != 0 && atq.start == atq.next) {
+ fprintf(stderr, "command queue full");
+ free(cmd);
+ return 1;
+ }
+
+ *atq.next = cmd;
+ atq.next = (char **)((atq.next + 1 - atq.ptrs) % QUEUE_MAX);
+ atq.count++;
+ return 0;
+}
+
+int cmd_call(const char *buf)
+{
+ char num[PHONE_NUMBER_MAX_LEN];
+ char *command;
+ size_t numlen = 0;
+
+ buf += 1;
+ while (buf) {
+ num[numlen++] = *buf;
+ }
+
+ /* TODO don't use malloc here */
+ command = malloc(numlen + 5);
+ if (!command)
+ return 1;
+
+ /* ATD is used to initiate a phone call */
+ if (snprintf(command, AT_MAX, "ATD%s;", num) > AT_MAX) {
+ free(command);
+ return 1;
+ }
+
+ return at_enqueue(command);
+}
+
+int cmd_answer() {
+ char *command = malloc(4);
+ if (!command)
+ return 1;
+
+ strcpy(command, "ATA");
+
+ return at_enqueue(command);
+}
+
+int cmd_hangup() {
+ char *command = malloc(4);
+ if (!command)
+ return 1;
+
+ strcpy(command, "ATH");
+
+ return at_enqueue(command);
+}
+
+int dispatch(const char *buf)
+{
+ switch (buf[0]) {
+ case CMD_DIAL: return cmd_call(buf);
+ case CMD_ANSWER: return cmd_answer(buf);
+ case CMD_HANGUP: return cmd_hangup(buf);
+ }
+}
+
+/* caller is responsible for freeing */
+char *at_sendnext(int fd)
+{
+ char *cmd = *atq.start;
+ size_t left = strlen(cmd);
+ ssize_t ret;
+ while (left) {
+ ret = write(fd, cmd, left);
+ if (ret == -1) {
+ fprintf(stderr, "failed write when sending command %s", cmd);
+ return NULL;
+ }
+ cmd += ret;
+ left -= ret;
+ }
+
+ cmd = *atq.start;
+ atq.start = (char **)((atq.start + 1 - atq.ptrs) % QUEUE_MAX);
+ atq.count--;
+ return cmd;
+}
+
+int main(int argc, char *argv[])
+{
+ argv0 = argv[0];
+
+ struct sockaddr_un sockaddr = {
+ .sun_family = AF_UNIX,
+ .sun_path = ATD_SOCKET ,
+ };
+
+ struct sockaddr_un backaddr = {
+ .sun_family = AF_UNIX,
+ .sun_path = "/tmp/atsim",
+ };
+
+ ssize_t len = 0, written = 0;
+ struct pollfd fds[MAX_FDS];
+ sigset_t mask;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+
+ if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
+ die("failed to block SIGINT:");
+
+ int sigintfd = signalfd(-1, &mask, 0);
+ if (sigintfd == -1)
+ die("failed to create signalfd:");
+
+ /* this is used to store read data from fds, and length */
+ struct fdbuf fdbufs[MAX_FDS] = {0};
+
+ for (int i = 0; i < MAX_FDS; i++)
+ fds[i].fd = -1;
+
+ int backsock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (backsock == -1)
+ die("failed to create backend socket:");
+
+ int back = connect(backsock, (struct sockaddr *) &backaddr, sizeof(struct sockaddr_un));
+ if (back == -1) {
+ die("failed to connect to backend:");
+ }
+
+ int sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sock == -1) {
+ die("failed to create socket:");
+ }
+
+ /* TODO replace these dies with warns and gotos so the socket file gets removed properly */
+ if (bind(sock, (struct sockaddr *) &sockaddr, sizeof(struct sockaddr_un)) == -1) {
+ die("failed to bind to socket:");
+ }
+
+ if (listen(sock, 50) == -1) {
+ die("failed to set socket to listening:");
+ }
+
+ fds[STDIN].fd = STDIN;
+ fds[STDIN].events = POLLIN;
+ fds[STDOUT].fd = STDOUT;
+ fds[STDOUT].events = 0;
+ fds[LISTENER].fd = sock;
+ fds[LISTENER].events = POLLIN;
+ fds[BACKEND].fd = back;
+ fds[BACKEND].events = 0;
+ fds[SIGNALINT].fd = sigintfd;
+ fds[SIGNALINT].events = POLLIN;
+
+ while (true) {
+ if (poll(fds, sizeof(fds) / sizeof(fds[0]), -1) == -1) {
+ warn("poll failed");
+ break;
+ }
+
+ /* handle interrupt */
+ if ((fds[SIGNALINT].revents & POLLIN) || (fds[LISTENER].revents & SIGHUP)) {
+ warn("time to die");
+ break;
+ }
+
+ for (int i = RSRVD_FDS; i < MAX_FDS; i++) {
+ if (fds[i].revents & POLLHUP) {
+ close(fds[i].fd);
+ fds[i].fd = -1;
+ } else if (fds[i].revents & POLLIN) {
+ /* TODO not this */
+ close(fds[i].fd);
+ fds[i].fd = -1;
+ }
+ }
+
+ /* TODO hook stdin up to command input? */
+ if (fds[STDIN].revents & POLLIN) {
+ len = read(fds[STDIN].fd, &fdbufs[STDIN].buf, BUFSIZE);
+ if (len == -1) {
+ warn("failed to read from stdin");
+ break;
+ }
+
+ fdbufs[STDIN].len = len;
+ fdbufs[STDIN].ptr = fdbufs[STDIN].buf;
+ fds[STDIN].events &= ~POLLIN;
+ fds[STDOUT].events |= POLLOUT;
+ }
+
+ /* TODO write to stdout when a command is received */
+ if (fds[STDOUT].revents & POLLOUT) {
+ int wr = write(fds[STDOUT].fd, &fdbufs[STDIN].buf, fdbufs[STDIN].len);
+ if (wr == -1) {
+ warn("failed to write to stdout");
+ break;
+ }
+
+ fdbufs[STDIN].len -= wr;
+
+ fdbufs[BACKEND].len -= wr;
+ fdbufs[STDIN].ptr += wr;
+ if (fdbufs[STDIN].len == 0) {
+ fds[STDOUT].events &= ~POLLOUT;
+ fds[STDIN].events |= POLLIN;
+ }
+ }
+
+ if (fds[LISTENER].revents & POLLIN) {
+ for (int i = RSRVD_FDS; i < MAX_FDS; i++) {
+ if (fds[i].fd != -1)
+ continue;
+
+ fds[i].fd = accept(fds[LISTENER].fd, NULL, NULL);
+ if (fds[i].fd == -1) {
+ warn("failed to accept connection");
+ break;
+ }
+ fds[i].events = POLLIN;
+ warn("accepted connection!");
+ break;
+ }
+ }
+ }
+
+ for (int i = 0; i < MAX_FDS; i++) {
+ if (fds[i].fd > STDERR)
+ close(fds[i].fd);
+ }
+ unlink(ATD_SOCKET);
+}
diff --git a/atd.h b/atd.h
@@ -0,0 +1,18 @@
+#define PHONE_NUMBER_MAX_LEN 15
+#define DIALING_DIGITS "0123456789*#+ABC"
+
+enum ops {
+ CMD_NONE = 0,
+ CMD_DIAL,
+ CMD_ANSWER,
+ CMD_HANGUP,
+};
+
+struct command {
+ enum ops op;
+ void *data;
+};
+
+struct data_dial {
+ char num[20];
+};
diff --git a/atsim.c b/atsim.c
@@ -0,0 +1,122 @@
+#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <poll.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+#define STDOUT 0
+#define STDIN 1
+#define SOCKFD 2
+#define FDCOUNT 3
+
+int main() {
+ struct sockaddr_un sockaddr = {
+ .sun_family = AF_UNIX,
+ .sun_path = "/tmp/atsim",
+ };
+ int sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ struct pollfd fds[FDCOUNT];
+
+ char tosock[1024];
+ char fromsock[1024];
+ char *fromoff = fromsock;
+ char *tooff = tosock;
+
+ ssize_t tocount;
+ ssize_t fromcount;
+
+ if (sock == -1) {
+ fprintf(stderr, "failed to create socket\n");
+ }
+
+ if (bind(sock, (struct sockaddr *) &sockaddr, sizeof(struct sockaddr_un)) == -1) {
+ fprintf(stderr, "failed to bind to socket\n");
+ goto err;
+ }
+
+ if (listen(sock, 1) != 0) {
+ fprintf(stderr, "failed to listen on socket\n");
+ goto err;
+ }
+
+ fds[SOCKFD].fd = accept(sock, NULL, NULL);
+ if (fds[SOCKFD].fd == -1) {
+ fprintf(stderr, "failed to accept connection\n");
+ goto err;
+ }
+
+ fds[SOCKFD].events = POLLIN;
+ fds[STDIN].fd = 1;
+ fds[STDIN].events = POLLIN;
+ fds[STDOUT].fd = 0;
+ fds[STDOUT].events = 0;
+
+ while (true) {
+ if (poll(fds, 2, -1) == -1) {
+ fprintf(stderr, "poll failed");
+ }
+
+ /* can read from socket */
+ if (fds[SOCKFD].revents & POLLIN) {
+ int ret = read(fds[SOCKFD].fd, fromsock, 1024);
+ if (ret == -1) {
+ fprintf(stderr, "failed to read from socket\n");
+ return 1;
+ }
+ fromcount = ret;
+ fromoff = fromsock;
+ fds[SOCKFD].events &= ~POLLIN;
+ fds[STDOUT].events |= POLLOUT;
+ }
+
+ if (fds[STDOUT].events & POLLOUT) {
+ int ret = write(fds[STDOUT].fd, fromoff, fromcount);
+ if (ret == -1) {
+ fprintf(stderr, "failed to write to stdout\n");
+ return 1;
+ }
+ fromcount -= ret;
+ fromoff += ret;
+
+ /* we are done writing to stdout, so we can resume to reading */
+ if (fromcount == 0) {
+ fds[STDOUT].events &= ~POLLOUT;
+ fds[SOCKFD].events |= POLLIN;
+ }
+ }
+
+ if (fds[SOCKFD].events & POLLOUT) {
+ int ret = write(fds[SOCKFD].fd, tooff, tocount);
+ if (ret == -1) {
+ fprintf(stderr, "failed to write to stdout\n");
+ return 1;
+ }
+ tocount -= ret;
+ tooff += ret;
+
+ /* we are done writing to stdout, so we can resume reading */
+ if (fromcount == 0) {
+ fds[SOCKFD].events &= ~POLLOUT;
+ fds[STDIN].events |= POLLIN;
+ }
+ }
+
+ if (fds[STDIN].revents & POLLIN) {
+ int ret = read(fds[STDIN].fd, tosock, 1024);
+ if (ret == -1) {
+ fprintf(stderr, "failed to read from stdin\n");
+ return 1;
+ }
+ tocount = ret;
+ tooff = fromsock;
+ fds[STDIN].events &= ~POLLIN;
+ fds[SOCKFD].events |= POLLOUT;
+ }
+ }
+
+ close(fds[SOCKFD].fd);
+err:
+ close(sock);
+ return 1;
+}
diff --git a/util.c b/util.c
@@ -0,0 +1,72 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+
+static void
+verr(const char *fmt, va_list ap)
+{
+ if (argv0 && strncmp(fmt, "usage", sizeof("usage") - 1)) {
+ fprintf(stderr, "%s: ", argv0);
+ }
+
+ vfprintf(stderr, fmt, ap);
+
+ if (fmt[0] && fmt[strlen(fmt) - 1] == ':') {
+ fputc(' ', stderr);
+ perror(NULL);
+ } else {
+ fputc('\n', stderr);
+ }
+}
+
+void
+warn(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ verr(fmt, ap);
+ va_end(ap);
+}
+
+void
+die(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ verr(fmt, ap);
+ va_end(ap);
+
+ exit(1);
+}
+
+void
+epledge(const char *promises, const char *execpromises)
+{
+ (void)promises;
+ (void)execpromises;
+
+#ifdef __OpenBSD__
+ if (pledge(promises, execpromises) == -1) {
+ die("pledge:");
+ }
+#endif /* __OpenBSD__ */
+}
+
+void
+eunveil(const char *path, const char *permissions)
+{
+ (void)path;
+ (void)permissions;
+
+#ifdef __OpenBSD__
+ if (unveil(path, permissions) == -1) {
+ die("unveil:");
+ }
+#endif /* __OpenBSD__ */
+}
diff --git a/util.h b/util.h
@@ -0,0 +1,20 @@
+/* See LICENSE file for copyright and license details. */
+#ifndef UTIL_H
+#define UTIL_H
+
+#undef MIN
+#define MIN(x,y) ((x) < (y) ? (x) : (y))
+#undef MAX
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+#undef LEN
+#define LEN(x) (sizeof (x) / sizeof *(x))
+
+extern char *argv0;
+
+void warn(const char *, ...);
+void die(const char *, ...);
+
+void epledge(const char *, const char *);
+void eunveil(const char *, const char *);
+
+#endif /* UTIL_H */