summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc.h2
-rw-r--r--net.c119
-rw-r--r--net.h1
3 files changed, 119 insertions, 3 deletions
diff --git a/doc.h b/doc.h
index 972134b..01a8235 100644
--- a/doc.h
+++ b/doc.h
@@ -12,7 +12,7 @@ struct doc_line {
enum doc_type {
DOC_UNKNOWN,
- DOC_GOPHERDOC,
+ DOC_GOPHERMAP,
DOC_GEMTEXT,
DOC_PLAIN,
};
diff --git a/net.c b/net.c
index 5d68714..6fbfa97 100644
--- a/net.c
+++ b/net.c
@@ -1,3 +1,11 @@
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+
+#include <stdlib.h>
+#include <errno.h>
#include <string.h>
#include <stdio.h>
@@ -41,6 +49,29 @@ int net_addr(const char *url, struct addr *adr, enum protocol prot_default) {
adr->host[adr->host_len++] = *url++;
}
adr->host[adr->host_len] = 0;
+
+ char *colon = strchr(adr->host, ':');
+ if (colon) {
+ errno = 0;
+ int port = strtoul(colon, NULL, 10);
+ if (errno) {
+ return -1;
+ } else {
+ adr->port = port;
+ }
+ *colon = 0;
+ adr->host_len = colon - adr->host;
+ } else {
+ switch (adr->prot) {
+ case PROT_GOPHER:
+ adr->port = 70;
+ break;
+ default:
+ adr->port = -1;
+ break;
+ }
+ }
+
if (*url && *url != '/') {
perr("hostname too long");
return -1;
@@ -60,7 +91,39 @@ int net_addr(const char *url, struct addr *adr, enum protocol prot_default) {
return 0;
}
-static int file_fetch(const struct addr *adr, struct buf *buf, enum doc_type *doct) {
+static long
+net_hosttoaddr(const char *hostname)
+{
+ struct hostent *h;
+ struct in_addr **al;
+ h = gethostbyname(hostname);
+ if (!h) {
+ perr("host not found");
+ return -1;
+ }
+ al = (struct in_addr **)h->h_addr_list;
+ if (!al || !(*al))
+ perr("empty host address");
+ return (*al)->s_addr;
+}
+
+static int
+net_conntohost(const char *hostname, int port)
+{
+ struct sockaddr_in sai;
+ int s;
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (!s)
+ return -1;
+ sai.sin_family = AF_INET;
+ sai.sin_port = htons(port);
+ sai.sin_addr.s_addr = net_hosttoaddr(hostname);
+ if (connect(s, (struct sockaddr *)&sai, sizeof(sai)))
+ return -1;
+ return s;
+}
+
+static int fetch_file(const struct addr *adr, struct buf *buf, enum doc_type *doct) {
FILE *f = fopen(adr->path, "r/o");
if (!f) {
perr("file not found");
@@ -77,10 +140,62 @@ static int file_fetch(const struct addr *adr, struct buf *buf, enum doc_type *do
return 0;
}
+static int fetch_gopher(const struct addr *adr, struct buf *buf, enum doc_type *doct) {
+ const char *sel = adr->path;
+ size_t sel_len = adr->path_len;
+ if (sel_len > 0 && sel[0] == '/') {
+ sel_len--;
+ sel++;
+ }
+ if (sel_len > 0) {
+ switch (*sel) {
+ case '0':
+ *doct = DOC_PLAIN;
+ break;
+ case '1':
+ *doct = DOC_GOPHERMAP;
+ break;
+ default:
+ perr("invalid item type");
+ return -1;
+ }
+ sel++;
+ sel_len--;
+ } else {
+ *doct = DOC_GOPHERMAP;
+ }
+ int s = net_conntohost(adr->host, adr->port);
+ if (s < 0) {
+ return -1;
+ }
+ if (write(s, sel, sel_len) < 0
+ || write(s, "\r\n", 2) < 0) {
+ perr("write failure");
+ return -1;
+ }
+ buf_init(buf, 64);
+ char inbuf[256];
+ for (;;) {
+ ssize_t n = read(s, inbuf, sizeof inbuf);
+ if (n < 0) {
+ perr("read error");
+ buf_free(buf);
+ close(s);
+ return -1;
+ }
+ if (!n) break;
+ buf_cat(buf, inbuf, n);
+ }
+ close(s);
+ return 0;
+}
+
int net_fetch(const struct addr *adr, struct buf *buf, enum doc_type *doct) {
switch (adr->prot) {
case PROT_FILE:
- return file_fetch(adr, buf, doct);
+ return fetch_file(adr, buf, doct);
+ case PROT_GOPHER:
+ return fetch_gopher(adr, buf, doct);
default:
perr("unsupported protocol");
return -1;
diff --git a/net.h b/net.h
index 64048be..a2ad569 100644
--- a/net.h
+++ b/net.h
@@ -19,6 +19,7 @@ struct addr {
char path[PATH_MAX + 1];
size_t host_len, path_len;
enum protocol prot;
+ int port;
};
int net_addr(const char *url, struct addr *adr, enum protocol prot_default);