atd

Unnamed repository; edit this file 'description' to name the repository.
git clone git://git.nihaljere.xyz/atd
Log | Files | Refs

commit 99cd88f50ee31bd859b9baa4e60e872e6ad9e213
Author: Nihal Jere <nihal@nihaljere.xyz>
Date:   Sun, 25 Apr 2021 01:09:29 -0500

initial commit

Diffstat:
AMakefile | 22++++++++++++++++++++++
Aatc.c | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aatd.c | 281+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aatd.h | 18++++++++++++++++++
Aatsim.c | 122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Autil.c | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Autil.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 */