gawk

[old] Sed-like interface to the Gopher protocol
Log | Files | Refs | LICENSE

commit a8f3425f16aa4dc1ce56b45b5ad5e8bdde8fc866
parent fffd975b08d08b6e98a0cd262310fdb2009f1623
Author: Jacob R. Edwards <jacobouno@protonmail.com>
Date:   Wed,  6 Jan 2021 23:17:31 -0800

Move networking functions to net.c

Diffstat:
MMakefile | 2+-
Mcommand.c | 1+
Mconfig.def.h | 2+-
Mgawk.h | 3++-
Mmain.c | 153++-----------------------------------------------------------------------------
Anet.c | 157+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anet.h | 7+++++++
7 files changed, 171 insertions(+), 154 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,5 +1,5 @@ TARGET = gawk -OBJS = main.o util.o command.o strtonum.o +OBJS = main.o util.o command.o strtonum.o net.o # paths PREFIX = /usr/local diff --git a/command.c b/command.c @@ -22,6 +22,7 @@ #include <unistd.h> #include "gawk.h" +#include "net.h" #include "util.h" /* diff --git a/config.def.h b/config.def.h @@ -1,7 +1,7 @@ /* gawk configuration */ /* how long to wait for response in ms */ -static int const timeout = 5 * 1000; +int const timeout = 5000; /* character used to negate commands */ static char const negate = '!'; diff --git a/gawk.h b/gawk.h @@ -37,7 +37,8 @@ struct bind { command *f; }; -int gopher(char **, char const *, int); int gawk(char **); +extern int const timeout; + #endif /* _gawk_h */ diff --git a/main.c b/main.c @@ -15,19 +15,17 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -#include <sys/socket.h> #include <dirent.h> #include <errno.h> -#include <netdb.h> -#include <poll.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> -#include <string.h> #include <unistd.h> +#include <string.h> #include "command.h" #include "gawk.h" +#include "net.h" #include "util.h" #include "config.h" /* must be included last */ @@ -51,41 +49,6 @@ argsplit(char **argv, int size, char *s, char const *sep, int cat) return argc; } -int -resolve(char const *host, char const *port) -{ - int error, s; - struct addrinfo hints; - struct addrinfo *ai, *ai0; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - - error = getaddrinfo(host, port, &hints, &ai0); - if (error) { - warn(0, "unable to resolve '%s' (%s): %s", - host, port, gai_strerror(error)); - return -1; - } - - s = -1; - for (ai = ai0; ai && s == -1; ai = ai->ai_next) { - s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (s != -1 && connect(s, ai->ai_addr, ai->ai_addrlen) == -1) { - close(s); - s = -1; - } - } - freeaddrinfo(ai0); - - if (s == -1) { - warn(0, "unable to connect to '%s' at '%s'", host, port); - return -1; - } - return s; -} - void putprompt(char const *prompt) { @@ -131,118 +94,6 @@ too_big: } int -gph_send(int sock, char const *message) -{ - char tmp[MY_PATH_MAX]; - int n; - - n = strcspn(message, "?"); - if (message[n] == '\0') - n = send(sock, message, n, 0); - else { - strlcpy(tmp, message, n + 1); - tr(tmp + n, message + n, '?', ' '); - tmp[n] = '\t'; - n = send(sock, tmp, strlen(tmp), 0); - } - - if (n == -1) { - warn(errno, "unable to send message"); - return 1; - } - if (shutdown(sock, SHUT_WR)) { - warn(errno, "shutdown"); - return 1; - } - return 0; -} - -ssize_t -recvtxt(int s, FILE *output) -{ - FILE *input; - char *bufp; - char buf[MY_LINE_MAX]; - - input = fdopen(s, "r"); - if (input == NULL) { - warn(errno, "fdopen"); - return 1; - } - - /* - * While the specification says strip leading periods if - * there are multiple I think it must mean only one (see - * RFC 1436 Appendix). - */ - while ((bufp = fgets(buf, sizeof(buf), input)) != NULL) { - if (*bufp == '.') - if (strcmp(++bufp, "\r\n") == 0) - break; - if (fputs(bufp, output) == EOF) - return 1; - } - - return 0; -} - -int -recvbin(int s, FILE *output) -{ - char buf[MY_CHUNK_MAX]; - ssize_t bytes; - - while ((bytes = recv(s, buf, sizeof(buf), 0)) > 0) - /* NOTE: cast is safe, bytes _is_ >0 */ - if (fwrite(buf, 1, bytes, output) != (size_t)bytes) - return 1; - return 0; -} - -int -gph_recvto(int sock, char const *path, int bin) -{ - FILE *fp; - int status; - struct pollfd pfd; - - pfd.fd = sock; - pfd.events = POLLRDNORM; - if (poll(&pfd, 1, timeout) < 1) { - warn(errno, "Unable to recieve response"); - return 1; - } - - fp = wfopen(path, "w+"); - if (fp == NULL) - return 1; - - if (bin) - status = recvbin(sock, fp); - else status = recvtxt(sock, fp); - if (wfclose(fp) || status) - return 1; - return 0; -} - -int -gopher(char **addr, char const *path, int bin) -{ - int sock; - - sock = resolve(addr[AR_HOST], addr[AR_PORT]); - if (sock == -1) - return 1; - - if (gph_send(sock, addr[AR_PATH]) != 0 || - gph_recvto(sock, path, bin) != 0) { - close(sock); - return 1; - } - return close(sock); -} - -int gphsplit(char **item, int size, char *buf) { int n; diff --git a/net.c b/net.c @@ -0,0 +1,157 @@ +#include <sys/socket.h> +#include <errno.h> +#include <netdb.h> +#include <poll.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "gawk.h" +#include "util.h" + +int +resolve(char const *host, char const *port) +{ + int error, s; + struct addrinfo hints; + struct addrinfo *ai, *ai0; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + error = getaddrinfo(host, port, &hints, &ai0); + if (error) { + warn(0, "unable to resolve '%s' (%s): %s", + host, port, gai_strerror(error)); + return -1; + } + + s = -1; + for (ai = ai0; ai && s == -1; ai = ai->ai_next) { + s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (s != -1 && connect(s, ai->ai_addr, ai->ai_addrlen) == -1) { + close(s); + s = -1; + } + } + freeaddrinfo(ai0); + + if (s == -1) { + warn(0, "unable to connect to '%s' at '%s'", host, port); + return -1; + } + return s; +} + +int +gph_send(int sock, char const *message) +{ + char tmp[MY_PATH_MAX]; + int n; + + n = strcspn(message, "?"); + if (message[n] == '\0') + n = send(sock, message, n, 0); + else { + strlcpy(tmp, message, n + 1); + tr(tmp + n, message + n, '?', ' '); + tmp[n] = '\t'; + n = send(sock, tmp, strlen(tmp), 0); + } + + if (n == -1) { + warn(errno, "unable to send message"); + return 1; + } + if (shutdown(sock, SHUT_WR)) { + warn(errno, "shutdown"); + return 1; + } + return 0; +} + +ssize_t +recvtxt(int s, FILE *output) +{ + FILE *input; + char *bufp; + char buf[MY_LINE_MAX]; + + input = fdopen(s, "r"); + if (input == NULL) { + warn(errno, "fdopen"); + return 1; + } + + /* + * While the specification says strip leading periods if + * there are multiple I think it must mean only one (see + * RFC 1436 Appendix). + */ + while ((bufp = fgets(buf, sizeof(buf), input)) != NULL) { + if (*bufp == '.') + if (strcmp(++bufp, "\r\n") == 0) + break; + if (fputs(bufp, output) == EOF) + return 1; + } + + return 0; +} + +int +recvbin(int s, FILE *output) +{ + char buf[MY_CHUNK_MAX]; + ssize_t bytes; + + while ((bytes = recv(s, buf, sizeof(buf), 0)) > 0) + /* NOTE: cast is safe, bytes _is_ >0 */ + if (fwrite(buf, 1, bytes, output) != (size_t)bytes) + return 1; + return 0; +} + +int +gph_recvto(int sock, char const *path, int bin) +{ + FILE *fp; + int status; + struct pollfd pfd; + + pfd.fd = sock; + pfd.events = POLLRDNORM; + if (poll(&pfd, 1, timeout) < 1) { + warn(errno, "Unable to recieve response"); + return 1; + } + + fp = wfopen(path, "w+"); + if (fp == NULL) + return 1; + + if (bin) + status = recvbin(sock, fp); + else status = recvtxt(sock, fp); + if (wfclose(fp) || status) + return 1; + return 0; +} + +int +gopher(char **addr, char const *path, int bin) +{ + int sock; + + sock = resolve(addr[AR_HOST], addr[AR_PORT]); + if (sock == -1) + return 1; + + if (gph_send(sock, addr[AR_PATH]) != 0 || + gph_recvto(sock, path, bin) != 0) { + close(sock); + return 1; + } + return close(sock); +} diff --git a/net.h b/net.h @@ -0,0 +1,7 @@ +#ifndef _net_h +#define _net_h + +int gopher(char **, char const *, int); +int resolve(char const *, char const *, int); + +#endif /* _net_h */