summaryrefslogtreecommitdiff
path: root/util.c
diff options
context:
space:
mode:
authorSunil Nimmagadda <sunil@nimmagadda.net>2024-09-14 12:32:01 +0530
committerSunil Nimmagadda <sunil@nimmagadda.net>2024-09-14 12:32:01 +0530
commit8f888fb1a3469b87e557efad93b293dd36288ba9 (patch)
tree6e8da124b6e9bb7ffe7a77f6e0c8cd811873cd06 /util.c
A HTTP(S), FTP client
Diffstat (limited to 'util.c')
-rw-r--r--util.c215
1 files changed, 215 insertions, 0 deletions
diff --git a/util.c b/util.c
new file mode 100644
index 0000000..463fb8b
--- /dev/null
+++ b/util.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2015 Sunil Nimmagadda <sunil@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/socket.h>
+
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "ftp.h"
+#include "xmalloc.h"
+
+static void tooslow(int);
+
+/*
+ * Wait for an asynchronous connect(2) attempt to finish.
+ */
+int
+connect_wait(int s)
+{
+ struct pollfd pfd[1];
+ int error = 0;
+ socklen_t len = sizeof(error);
+
+ pfd[0].fd = s;
+ pfd[0].events = POLLOUT;
+
+ if (poll(pfd, 1, -1) == -1)
+ return -1;
+ if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
+ return -1;
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
+
+static void
+tooslow(int signo)
+{
+ dprintf(STDERR_FILENO, "%s: connect taking too long\n", getprogname());
+ _exit(2);
+}
+
+int
+tcp_connect(const char *host, const char *port, int timeout)
+{
+ struct addrinfo hints, *res, *res0;
+ char hbuf[NI_MAXHOST];
+ const char *cause = NULL;
+ int error, s = -1, save_errno;
+
+ if (host == NULL) {
+ warnx("hostname missing");
+ return -1;
+ }
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_STREAM;
+ if ((error = getaddrinfo(host, port, &hints, &res0))) {
+ warnx("%s: %s", host, gai_strerror(error));
+ return -1;
+ }
+
+ if (timeout) {
+ (void)signal(SIGALRM, tooslow);
+ alarm(timeout);
+ }
+
+ for (res = res0; res; res = res->ai_next) {
+ if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf,
+ sizeof hbuf, NULL, 0, NI_NUMERICHOST) != 0)
+ (void)strlcpy(hbuf, "(unknown)", sizeof hbuf);
+
+ log_info("Trying %s...\n", hbuf);
+ s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (s == -1) {
+ cause = "socket";
+ continue;
+ }
+
+ for (error = connect(s, res->ai_addr, res->ai_addrlen);
+ error != 0 && errno == EINTR; error = connect_wait(s))
+ continue;
+
+ if (error != 0) {
+ cause = "connect";
+ save_errno = errno;
+ close(s);
+ errno = save_errno;
+ s = -1;
+ continue;
+ }
+
+ break;
+ }
+
+ freeaddrinfo(res0);
+ if (s == -1) {
+ warn("%s", cause);
+ return -1;
+ }
+
+ if (timeout) {
+ signal(SIGALRM, SIG_DFL);
+ alarm(0);
+ }
+
+ return s;
+}
+
+void
+log_info(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (verbose == 0)
+ return;
+
+ va_start(ap, fmt);
+ if (oarg && strcmp(oarg, "-") == 0)
+ vfprintf(stderr, fmt, ap);
+ else
+ vprintf(fmt, ap);
+
+ va_end(ap);
+}
+
+void
+log_request(const char *prefix, struct url *url, struct url *proxy)
+{
+ char *host;
+ int custom_port;
+
+ if (url->scheme == S_FILE)
+ return;
+
+ custom_port = strcmp(url->port, url_port_str(url->scheme)) ? 1 : 0;
+ if (url->ip_literal)
+ xasprintf(&host, "[%s]", url->host);
+ else
+ host = xstrdup(url->host);
+
+ if (proxy)
+ log_info("%s %s//%s%s%s%s"
+ " (via %s//%s%s%s)\n",
+ prefix,
+ url_scheme_str(url->scheme),
+ host,
+ custom_port ? ":" : "",
+ custom_port ? url->port : "",
+ url->path ? url->path : "",
+
+ /* via proxy part */
+ (proxy->scheme == S_HTTP) ? "http" : "https",
+ proxy->host,
+ proxy->port ? ":" : "",
+ proxy->port ? proxy->port : "");
+ else
+ log_info("%s %s//%s%s%s%s\n",
+ prefix,
+ url_scheme_str(url->scheme),
+ host,
+ custom_port ? ":" : "",
+ custom_port ? url->port : "",
+ url->path ? url->path : "");
+
+ free(host);
+}
+
+void
+copy_file(FILE *dst, FILE *src, off_t *offset)
+{
+ char *tmp_buf;
+ size_t r;
+
+ tmp_buf = xmalloc(TMPBUF_LEN);
+ while ((r = fread(tmp_buf, 1, TMPBUF_LEN, src)) != 0 && !interrupted) {
+ *offset += r;
+ if (fwrite(tmp_buf, 1, r, dst) != r)
+ err(1, "%s: fwrite", __func__);
+ }
+
+ if (interrupted) {
+ free(tmp_buf);
+ return;
+ }
+
+ if (!feof(src))
+ errx(1, "%s: fread", __func__);
+
+ free(tmp_buf);
+}