Add `gemini_envinit', add proxy support, simplfy header handling
Add `gemini_envinit' which initializes some parts of the structure from the environment. Add proxy support in the form of a `proxy' field in the structure that can hold a host string. Simplfy header handling in `gemini_read', removing the `GEMINI_STRICT' flag. Add the `-h' option to gemini-cat(1) to set the proxy host. Remove the `-e' option from gemini-cat(1). Add environment variable support to gemini-cat(1), including `GEMINI_TOFU_WRITE', which will set the corresponding flag.
This commit is contained in:
parent
e0d7ff540e
commit
5dcddc7077
46
gemini-cat.1
46
gemini-cat.1
|
@ -1,4 +1,4 @@
|
||||||
.Dd Jun 10, 2020
|
.Dd Jun 20, 2020
|
||||||
.Dt GEMINI-CAT 1
|
.Dt GEMINI-CAT 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
|
@ -6,8 +6,9 @@
|
||||||
.Nd concatnate responses from a Gemini URLs
|
.Nd concatnate responses from a Gemini URLs
|
||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm
|
.Nm
|
||||||
.Op Fl RSeisw
|
.Op Fl RSisw
|
||||||
.Op Fl c Ar cert-file
|
.Op Fl c Ar cert-file
|
||||||
|
.Op Fl h Ar proxy-host
|
||||||
.Op Fl k Ar key-file
|
.Op Fl k Ar key-file
|
||||||
.Op Fl p Ar port
|
.Op Fl p Ar port
|
||||||
.Op Fl r Ar max-redirects
|
.Op Fl r Ar max-redirects
|
||||||
|
@ -26,8 +27,10 @@ Strip out non-space control-characters before writing, the default for
|
||||||
TTY outputs.
|
TTY outputs.
|
||||||
.It Fl c Ar cert-file
|
.It Fl c Ar cert-file
|
||||||
The certificate file to use for TLS.
|
The certificate file to use for TLS.
|
||||||
.It Fl e
|
.It Fl h Ar proxy-host
|
||||||
Enforce a strict interpretation of the Gemini specification.
|
The host to connect through.
|
||||||
|
.Ar proxy-host
|
||||||
|
can also specify a port number.
|
||||||
.It Fl i
|
.It Fl i
|
||||||
Interactive mode (allow prompts).
|
Interactive mode (allow prompts).
|
||||||
.It Fl k Ar key-file
|
.It Fl k Ar key-file
|
||||||
|
@ -47,6 +50,30 @@ The file to read host certificate hashes from.
|
||||||
Write any new hashes to the
|
Write any new hashes to the
|
||||||
.Ar tofu-file .
|
.Ar tofu-file .
|
||||||
.El
|
.El
|
||||||
|
.Sh ENVIRONMENT
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It Ev GEMINI_KEYFILE
|
||||||
|
Sets the TLS key file before
|
||||||
|
.Fl k .
|
||||||
|
.It Ev GEMINI_CERTFILE
|
||||||
|
Sets the TLS certificate file before
|
||||||
|
.Fl c .
|
||||||
|
.It Ev GEMINI_TOFUFILE
|
||||||
|
Sets the TOFU tile before
|
||||||
|
.Fl t .
|
||||||
|
.It Ev GEMINI_PROXY
|
||||||
|
Sets the proxy host before
|
||||||
|
.Fl h .
|
||||||
|
.It Ev GEMINI_MAX_REDIRECTS
|
||||||
|
Sets the maximum number of redirects before
|
||||||
|
.Fl r .
|
||||||
|
.It Ev GEMINI_PORT
|
||||||
|
Sets the default port number before
|
||||||
|
.Fl p .
|
||||||
|
.It Ev GEMINI_TOFU_WRITE
|
||||||
|
If set to a non-empty string, it is equivalent to
|
||||||
|
.Fl w .
|
||||||
|
.El
|
||||||
.Sh EXIT STATUS
|
.Sh EXIT STATUS
|
||||||
.Ex -std
|
.Ex -std
|
||||||
.Sh EXAMPLES
|
.Sh EXAMPLES
|
||||||
|
@ -54,6 +81,15 @@ Fetch the
|
||||||
.Lk tilde.black
|
.Lk tilde.black
|
||||||
root index:
|
root index:
|
||||||
.Dl gemini-cat gemini://tilde.black/ | more
|
.Dl gemini-cat gemini://tilde.black/ | more
|
||||||
|
Fetch
|
||||||
|
.Lk tilde.black
|
||||||
|
from its onion address:
|
||||||
|
.Bd -literal -offset indent -compact
|
||||||
|
torsocks gemini-cat -h \\
|
||||||
|
black6kfjetfuzaeozz7fs53whh7xtd4e27telrf5fg5kgdt5ah5plad.onion \\
|
||||||
|
//tilde.black/
|
||||||
|
.Ed
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
.Xr tls_config_set_cert_file 3
|
.Xr libgeminiclient 3 ,
|
||||||
|
.Xr tls_config_set_cert_file 3 ,
|
||||||
.Xr tls_config_set_key_file 3
|
.Xr tls_config_set_key_file 3
|
||||||
|
|
18
gemini-cat.c
18
gemini-cat.c
|
@ -32,19 +32,23 @@ main(int argc, char *argv[])
|
||||||
char b[BUFFER_SIZE];
|
char b[BUFFER_SIZE];
|
||||||
struct gemini gemini = GEMINI_INITIALIZER;
|
struct gemini gemini = GEMINI_INITIALIZER;
|
||||||
const char *query = NULL;
|
const char *query = NULL;
|
||||||
|
char *p;
|
||||||
ssize_t r;
|
ssize_t r;
|
||||||
size_t i;
|
size_t i;
|
||||||
int prompt = 0;
|
int prompt = 0;
|
||||||
int rawout;
|
int rawout;
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
|
gemini_envinit(&gemini);
|
||||||
|
if ((p = getenv("GEMINI_TOFU_WRITE")) != NULL && *p != '\0')
|
||||||
|
gemini.flags |= GEMINI_TOFU_WRITE;
|
||||||
rawout = !isatty(STDOUT_FILENO);
|
rawout = !isatty(STDOUT_FILENO);
|
||||||
#ifdef __OpenBSD__
|
#ifdef __OpenBSD__
|
||||||
if (pledge("stdio cpath rpath wpath flock inet dns tty", NULL)
|
if (pledge("stdio cpath rpath wpath flock inet dns tty", NULL)
|
||||||
!= 0)
|
!= 0)
|
||||||
err(EX_NOPERM, "Failed pledge(2)");
|
err(EX_NOPERM, "Failed pledge(2)");
|
||||||
#endif
|
#endif
|
||||||
while ((c = getopt(argc, argv, "RSc:eik:p:r:st:w")) != -1)
|
while ((c = getopt(argc, argv, "RSc:h:ik:p:r:st:w")) != -1)
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'R':
|
case 'R':
|
||||||
rawout = 1;
|
rawout = 1;
|
||||||
|
@ -55,8 +59,8 @@ main(int argc, char *argv[])
|
||||||
case 'c':
|
case 'c':
|
||||||
gemini.certfile = optarg;
|
gemini.certfile = optarg;
|
||||||
break;
|
break;
|
||||||
case 'e':
|
case 'h':
|
||||||
gemini.flags |= GEMINI_STRICT;
|
gemini.proxy = optarg;
|
||||||
break;
|
break;
|
||||||
case 'i':
|
case 'i':
|
||||||
prompt = 1;
|
prompt = 1;
|
||||||
|
@ -157,10 +161,10 @@ err:
|
||||||
gemini_fini(&gemini);
|
gemini_fini(&gemini);
|
||||||
return (EX_SOFTWARE);
|
return (EX_SOFTWARE);
|
||||||
usage:
|
usage:
|
||||||
(void)fprintf(stderr, "usage: gemini-cat [-RSeisw] "
|
(void)fprintf(stderr, "usage: gemini-cat [-RSisw] "
|
||||||
"[-c cert-file] [-k key-file] [-p port]\n"
|
"[-c cert-file] [-h proxy-host] [-k key-file]\n"
|
||||||
" [-r max-redirects] [-t tofu-file] "
|
" [-p port] [-r max-redirects] "
|
||||||
"URL...\n");
|
"[-t tofu-file] URL...\n");
|
||||||
return (EX_USAGE);
|
return (EX_USAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
.Dd May 1, 2020
|
.Dd Jun 20, 2020
|
||||||
.Dt LIBGEMINICLIENT 3
|
.Dt LIBGEMINICLIENT 3
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
|
@ -11,6 +11,8 @@
|
||||||
.Fn gemini_create void
|
.Fn gemini_create void
|
||||||
.Ft void
|
.Ft void
|
||||||
.Fn gemini_init "struct gemini *ctx"
|
.Fn gemini_init "struct gemini *ctx"
|
||||||
|
.Ft void
|
||||||
|
.Fn gemini_envinit "struct gemini *ctx"
|
||||||
.Ft int
|
.Ft int
|
||||||
.Fn gemini_connect "struct gemini *ctx" "const char *url"
|
.Fn gemini_connect "struct gemini *ctx" "const char *url"
|
||||||
.Ft int
|
.Ft int
|
||||||
|
@ -37,6 +39,39 @@ Initializes the provided
|
||||||
.Vt gemini
|
.Vt gemini
|
||||||
structure.
|
structure.
|
||||||
.Pp
|
.Pp
|
||||||
|
.Fn gemini_envinit
|
||||||
|
Initializes the provided
|
||||||
|
.Vt gemini
|
||||||
|
structure, using the following environment variables,
|
||||||
|
if they are provided:
|
||||||
|
.Pp
|
||||||
|
.Bl -tag -width Ds -compact
|
||||||
|
.It Ev GEMINI_KEYFILE
|
||||||
|
Initializes the
|
||||||
|
.Fa keyfile
|
||||||
|
field.
|
||||||
|
.It Ev GEMINI_CERTFILE
|
||||||
|
Initializes the
|
||||||
|
.Fa certfile
|
||||||
|
field.
|
||||||
|
.It Ev GEMINI_TOFUFILE
|
||||||
|
Initializes the
|
||||||
|
.Fa tofufile
|
||||||
|
field.
|
||||||
|
.It Ev GEMINI_PROXY
|
||||||
|
Initializes the
|
||||||
|
.Fa proxy
|
||||||
|
field.
|
||||||
|
.It Ev GEMINI_MAX_REDIRECTS
|
||||||
|
Initializes the
|
||||||
|
.Fa maxredirects
|
||||||
|
field.
|
||||||
|
.It Ev GEMINI_PORT
|
||||||
|
Initializes the
|
||||||
|
.Fa port
|
||||||
|
field.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
.Fn gemini_connect
|
.Fn gemini_connect
|
||||||
opens a connection to the provided
|
opens a connection to the provided
|
||||||
.Fa url ,
|
.Fa url ,
|
||||||
|
@ -123,16 +158,17 @@ The useful members of the
|
||||||
structure are as follows:
|
structure are as follows:
|
||||||
.Bd -literal -offset indent -compact
|
.Bd -literal -offset indent -compact
|
||||||
struct gemini {
|
struct gemini {
|
||||||
char meta[GEMINI_META_MAX + 1];
|
const char *certfile; /* Certificate file */
|
||||||
const char *certfile; /* Certificate file */
|
const char *keyfile; /* Key file */
|
||||||
const char *keyfile; /* Key file */
|
const char *tofufile; /* Known-hosts file */
|
||||||
const char *tofufile; /* Known-hosts file */
|
const char *proxy; /* Proxy host */
|
||||||
size_t metalen; /* Length of `meta' */
|
char *meta; /* Response META */
|
||||||
int flags; /* Configuration flags */
|
size_t metalen; /* Length of `meta' */
|
||||||
int maxredirects; /* Default 5 */
|
int flags; /* Configuration flags */
|
||||||
int socketfd; /* The socket to use */
|
int maxredirects; /* Default 5 */
|
||||||
short status; /* Response STATUS */
|
int socketfd; /* The socket to use */
|
||||||
short port; /* Default port */
|
short status; /* Response STATUS */
|
||||||
|
short port; /* Default port */
|
||||||
};
|
};
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
|
@ -145,8 +181,6 @@ Write any new certificate information to
|
||||||
.Fa tofufile
|
.Fa tofufile
|
||||||
on
|
on
|
||||||
.Dn gemini_close .
|
.Dn gemini_close .
|
||||||
.It Dv GEMINI_STRICT
|
|
||||||
Enforce a strict interpretation of the Gemini protocol.
|
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
All knows
|
All knows
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -21,8 +23,6 @@ enum {
|
||||||
GEMINI_STATE_INIT = 0,
|
GEMINI_STATE_INIT = 0,
|
||||||
GEMINI_STATE_REQUEST,
|
GEMINI_STATE_REQUEST,
|
||||||
GEMINI_STATE_RESPONSE,
|
GEMINI_STATE_RESPONSE,
|
||||||
GEMINI_STATE_RESPONSE_WHITESPACE,
|
|
||||||
GEMINI_STATE_RESPONSE_META,
|
|
||||||
GEMINI_STATE_READ,
|
GEMINI_STATE_READ,
|
||||||
GEMINI_STATE_DONE,
|
GEMINI_STATE_DONE,
|
||||||
GEMINI_STATE_ERROR
|
GEMINI_STATE_ERROR
|
||||||
|
@ -32,6 +32,7 @@ enum {
|
||||||
#define GEMINI_DEFAULT_PORT 1965
|
#define GEMINI_DEFAULT_PORT 1965
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define ISASCIIBLANK(c) ((c) == '\t' || (c) == ' ')
|
||||||
#define ISASCIILOWER(c) ((c) >= 'a' && (c) <= 'z')
|
#define ISASCIILOWER(c) ((c) >= 'a' && (c) <= 'z')
|
||||||
#define ISASCIIUPPER(c) ((c) >= 'A' && (c) <= 'Z')
|
#define ISASCIIUPPER(c) ((c) >= 'A' && (c) <= 'Z')
|
||||||
#define ISASCIIDIGIT(c) ((c) >= '0' && (c) <= '9')
|
#define ISASCIIDIGIT(c) ((c) >= '0' && (c) <= '9')
|
||||||
|
@ -65,8 +66,13 @@ gemini_init(struct gemini *g)
|
||||||
g->certmem = NULL;
|
g->certmem = NULL;
|
||||||
g->keyfile = NULL;
|
g->keyfile = NULL;
|
||||||
g->certfile = NULL;
|
g->certfile = NULL;
|
||||||
g->metalen = 0;
|
g->proxy = NULL;
|
||||||
|
g->meta = NULL;
|
||||||
|
g->extra = NULL;
|
||||||
g->reqlen = 0;
|
g->reqlen = 0;
|
||||||
|
g->reslen = 0;
|
||||||
|
g->metalen = 0;
|
||||||
|
g->extralen = 0;
|
||||||
g->keylen = 0;
|
g->keylen = 0;
|
||||||
g->certlen = 0;
|
g->certlen = 0;
|
||||||
g->index = 0;
|
g->index = 0;
|
||||||
|
@ -81,6 +87,44 @@ gemini_init(struct gemini *g)
|
||||||
g->port = 0;
|
g->port = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gemini_envinit(struct gemini *g)
|
||||||
|
{
|
||||||
|
const char *s;
|
||||||
|
char *p;
|
||||||
|
unsigned long ul;
|
||||||
|
|
||||||
|
gemini_init(g);
|
||||||
|
g->keyfile = getenv("GEMINI_KEYFILE");
|
||||||
|
g->certfile = getenv("GEMINI_CERTFILE");
|
||||||
|
g->certfile = getenv("GEMINI_TOFUFILE");
|
||||||
|
g->proxy = getenv("GEMINI_PROXY");
|
||||||
|
if ((s = getenv("GEMINI_MAX_REDIRECTS")) != NULL) {
|
||||||
|
errno = 0;
|
||||||
|
ul = strtoul(s, &p, 0);
|
||||||
|
if (errno == 0 && (p == s || *p != '\0'))
|
||||||
|
errno = EINVAL;
|
||||||
|
if (errno == 0 && ul > INT_MAX)
|
||||||
|
errno = ERANGE;
|
||||||
|
if (errno == 0)
|
||||||
|
g->maxredirects = ul;
|
||||||
|
else
|
||||||
|
warn("GEMINI_MAX_REDIRECTS");
|
||||||
|
}
|
||||||
|
if ((s = getenv("GEMINI_PORT")) != NULL) {
|
||||||
|
errno = 0;
|
||||||
|
ul = strtoul(s, &p, 0);
|
||||||
|
if (errno == 0 && (p == s || *p != '\0'))
|
||||||
|
errno = EINVAL;
|
||||||
|
if (errno == 0 && ul > SHRT_MAX)
|
||||||
|
errno = ERANGE;
|
||||||
|
if (errno == 0)
|
||||||
|
g->port = ul;
|
||||||
|
else
|
||||||
|
warn("GEMINI_PORT");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
gemini_reset(struct gemini *g)
|
gemini_reset(struct gemini *g)
|
||||||
{
|
{
|
||||||
|
@ -95,8 +139,12 @@ gemini_reset(struct gemini *g)
|
||||||
}
|
}
|
||||||
g->tls = NULL;
|
g->tls = NULL;
|
||||||
g->tls_config = NULL;
|
g->tls_config = NULL;
|
||||||
g->metalen = 0;
|
g->meta = NULL;
|
||||||
|
g->extra = NULL;
|
||||||
g->reqlen = 0;
|
g->reqlen = 0;
|
||||||
|
g->reslen = 0;
|
||||||
|
g->metalen = 0;
|
||||||
|
g->extralen = 0;
|
||||||
g->index = 0;
|
g->index = 0;
|
||||||
g->state = GEMINI_STATE_INIT;
|
g->state = GEMINI_STATE_INIT;
|
||||||
g->status = 0;
|
g->status = 0;
|
||||||
|
@ -177,6 +225,12 @@ gemini_connect(struct gemini *g, const char *url)
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
/* Proxy */
|
||||||
|
if (g->proxy != NULL && strlcpy(g->request, g->proxy,
|
||||||
|
sizeof(g->request)) >= sizeof(g->request)) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
/* Initialize TLS */
|
/* Initialize TLS */
|
||||||
if ((g->tls_config = tls_config_new()) == NULL)
|
if ((g->tls_config = tls_config_new()) == NULL)
|
||||||
return (-1);
|
return (-1);
|
||||||
|
@ -276,7 +330,6 @@ gemini_read(struct gemini *g, void *b, size_t bs)
|
||||||
size_t j;
|
size_t j;
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
cb = b;
|
|
||||||
if (g->state == GEMINI_STATE_ERROR)
|
if (g->state == GEMINI_STATE_ERROR)
|
||||||
return (-1);
|
return (-1);
|
||||||
if (g->state == GEMINI_STATE_DONE)
|
if (g->state == GEMINI_STATE_DONE)
|
||||||
|
@ -289,113 +342,87 @@ gemini_read(struct gemini *g, void *b, size_t bs)
|
||||||
goto errt;
|
goto errt;
|
||||||
g->index += r;
|
g->index += r;
|
||||||
}
|
}
|
||||||
g->status = 0;
|
g->reslen = 0;
|
||||||
g->index = 0;
|
g->index = 0;
|
||||||
|
g->status = 0;
|
||||||
g->state = GEMINI_STATE_RESPONSE;
|
g->state = GEMINI_STATE_RESPONSE;
|
||||||
}
|
}
|
||||||
for (;;) {
|
if (g->state == GEMINI_STATE_RESPONSE) {
|
||||||
i = i != r ? i : 0;
|
do {
|
||||||
if (i == 0 && (r = tls_read(g->tls, b, bs)) <= 0)
|
if (g->reslen >= sizeof(g->response))
|
||||||
goto errt;
|
goto errinval;
|
||||||
switch (g->state) {
|
if ((r = tls_read(g->tls,
|
||||||
case GEMINI_STATE_RESPONSE:
|
g->response + g->reslen,
|
||||||
/* This is (probably) over-engineered. */
|
sizeof(g->response) - g->reslen)) <= 0)
|
||||||
for (; i < r && g->index < 2; i++, g->index++) {
|
goto errt;
|
||||||
if (!ISASCIIDIGIT(cb[i]))
|
for (i = g->reslen, g->reslen += r;
|
||||||
goto errinval;
|
i < g->reslen && g->response[i] != '\n';
|
||||||
g->status =
|
|
||||||
g->status * 10 + (cb[i] - '0');
|
|
||||||
}
|
|
||||||
if (g->index < 2)
|
|
||||||
break;
|
|
||||||
g->state = GEMINI_STATE_RESPONSE_WHITESPACE;
|
|
||||||
break;
|
|
||||||
case GEMINI_STATE_RESPONSE_WHITESPACE:
|
|
||||||
/* The unspecified maximum is kind-of absurd. */
|
|
||||||
for (; i < r && (cb[i] == ' ' || cb[i] == '\t');
|
|
||||||
i++)
|
i++)
|
||||||
/* do nothing */;
|
/* do nothing */;
|
||||||
if (i == r)
|
} while (i == g->reslen);
|
||||||
break;
|
g->extra = g->response + i + 1;
|
||||||
g->metalen = 0;
|
g->extralen = g->reslen - i - 1;
|
||||||
g->state = GEMINI_STATE_RESPONSE_META;
|
g->reslen = i - (g->response[i - 1] == '\r');
|
||||||
|
g->response[g->reslen] = '\0';
|
||||||
|
if (g->reslen < 2)
|
||||||
|
goto errinval;
|
||||||
|
if (!ISASCIIDIGIT(g->response[0]) ||
|
||||||
|
!ISASCIIDIGIT(g->response[1]))
|
||||||
|
goto errinval;
|
||||||
|
if (g->reslen > 2 && !ISASCIIBLANK(g->response[2]))
|
||||||
|
goto errinval;
|
||||||
|
g->meta = g->response + (g->reslen > 2 ? 3 : 2);
|
||||||
|
g->metalen = g->reslen > 2 ? g->reslen - 3 : 0;
|
||||||
|
g->status = (g->response[0] - '0') * 10 +
|
||||||
|
(g->response[1] - '0');
|
||||||
|
if (g->response[0] != '3')
|
||||||
|
g->redirects = 0;
|
||||||
|
if (g->response[0] != '2' && g->extralen > 0)
|
||||||
|
goto errinval;
|
||||||
|
switch (g->response[0]) {
|
||||||
|
case '0':
|
||||||
|
goto errinval;
|
||||||
|
case '1':
|
||||||
|
goto done;
|
||||||
|
case '2':
|
||||||
|
g->index = 0;
|
||||||
|
g->state = GEMINI_STATE_READ;
|
||||||
break;
|
break;
|
||||||
case GEMINI_STATE_RESPONSE_META:
|
case '3':
|
||||||
/*
|
if (g->maxredirects == 0)
|
||||||
* If the white-space had a reasonable maximum
|
|
||||||
* then this would be easier.
|
|
||||||
*/
|
|
||||||
for (j = i; j < r && cb[j] != '\n'; j++)
|
|
||||||
/* do nothing */;
|
|
||||||
if (g->metalen + j - i > sizeof(g->meta))
|
|
||||||
goto errinval;
|
|
||||||
(void)memcpy(g->meta + g->metalen, cb + i,
|
|
||||||
j - i);
|
|
||||||
g->metalen += j - i;
|
|
||||||
if (cb[j] != '\n') {
|
|
||||||
i = j;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (g->metalen > 0 &&
|
|
||||||
g->meta[g->metalen - 1] == '\r') {
|
|
||||||
g->metalen--;
|
|
||||||
} else if (g->flags & GEMINI_STRICT) {
|
|
||||||
if (g->metalen == GEMINI_META_MAX)
|
|
||||||
goto errinval;
|
|
||||||
g->meta[g->metalen++] = '\n';
|
|
||||||
i = j + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (g->metalen > GEMINI_META_MAX)
|
|
||||||
goto errinval;
|
|
||||||
g->meta[g->metalen] = '\0';
|
|
||||||
i = j + 1;
|
|
||||||
if (g->status / 10 != 3)
|
|
||||||
g->redirects = 0;
|
|
||||||
switch (g->status / 10) {
|
|
||||||
case 0:
|
|
||||||
goto errinval;
|
|
||||||
case 1:
|
|
||||||
goto done;
|
goto done;
|
||||||
case 2:
|
if (g->redirects >= g->maxredirects)
|
||||||
g->state = GEMINI_STATE_READ;
|
goto errredir;
|
||||||
break;
|
g->redirects++;
|
||||||
case 3:
|
if (gemini_connect_query(g, g->meta, NULL) != 0)
|
||||||
if (g->maxredirects == 0)
|
goto err;
|
||||||
goto done;
|
return (gemini_read(g, b, bs));
|
||||||
if (g->redirects >= g->maxredirects)
|
case '4':
|
||||||
goto errredir;
|
goto errg;
|
||||||
g->redirects++;
|
case '5':
|
||||||
if (gemini_connect_query(g, g->meta,
|
goto errg;
|
||||||
NULL) != 0)
|
case '6':
|
||||||
goto err;
|
goto done;
|
||||||
return (gemini_read(g, b, bs));
|
case '7':
|
||||||
case 4:
|
goto errinval;
|
||||||
goto errg;
|
case '8':
|
||||||
case 5:
|
goto errinval;
|
||||||
goto errg;
|
case '9':
|
||||||
case 6:
|
goto errinval;
|
||||||
goto done;
|
|
||||||
case 7:
|
|
||||||
goto errinval;
|
|
||||||
case 8:
|
|
||||||
goto errinval;
|
|
||||||
case 9:
|
|
||||||
goto errinval;
|
|
||||||
default:
|
|
||||||
abort(); /* unreachable */
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GEMINI_STATE_READ:
|
|
||||||
if (i > 0) {
|
|
||||||
(void)memmove(cb, cb + i, r - i);
|
|
||||||
r -= i;
|
|
||||||
}
|
|
||||||
return (r);
|
|
||||||
default:
|
default:
|
||||||
abort(); /* unreachable */;
|
abort(); /* unreachable */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (g->state != GEMINI_STATE_READ)
|
||||||
|
abort(); /* unreachable */
|
||||||
|
if (g->index < g->extralen) {
|
||||||
|
i = g->extralen - g->index;
|
||||||
|
i = i < bs ? i : bs;
|
||||||
|
(void)memcpy(b, g->extra + g->index, i);
|
||||||
|
g->index += i;
|
||||||
|
return (i);
|
||||||
|
}
|
||||||
|
return (tls_read(g->tls, b, bs));
|
||||||
eof:
|
eof:
|
||||||
if (g->state != GEMINI_STATE_READ) {
|
if (g->state != GEMINI_STATE_READ) {
|
||||||
warnx("Unexpected EOF");
|
warnx("Unexpected EOF");
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
#define GEMINI_REDIRECT_MAX 5
|
#define GEMINI_REDIRECT_MAX 5
|
||||||
|
|
||||||
#define GEMINI_TOFU_WRITE 0x01
|
#define GEMINI_TOFU_WRITE 0x01
|
||||||
#define GEMINI_STRICT 0x02
|
|
||||||
|
|
||||||
struct gemini_tofu {
|
struct gemini_tofu {
|
||||||
struct gemini_tofu *next;
|
struct gemini_tofu *next;
|
||||||
|
@ -24,10 +23,10 @@ struct gemini_tofu {
|
||||||
|
|
||||||
#define GEMINI_INITIALIZER \
|
#define GEMINI_INITIALIZER \
|
||||||
{ { 0 }, { 0 }, NULL, NULL, NULL, NULL, NULL, NULL, \
|
{ { 0 }, { 0 }, NULL, NULL, NULL, NULL, NULL, NULL, \
|
||||||
NULL, NULL, 0, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, \
|
NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, \
|
||||||
GEMINI_REDIRECT_MAX, 0, 0 }
|
-1, -1, 0, 0, 0, 0, GEMINI_REDIRECT_MAX, 0, 0 }
|
||||||
struct gemini {
|
struct gemini {
|
||||||
char meta[GEMINI_META_MAX + 1];
|
char response[3 + GEMINI_META_MAX + 2];
|
||||||
char request[GEMINI_URL_MAX + 2];
|
char request[GEMINI_URL_MAX + 2];
|
||||||
struct tls *tls;
|
struct tls *tls;
|
||||||
struct tls_config *tls_config;
|
struct tls_config *tls_config;
|
||||||
|
@ -37,8 +36,13 @@ struct gemini {
|
||||||
const char *keyfile;
|
const char *keyfile;
|
||||||
const char *certfile;
|
const char *certfile;
|
||||||
const char *tofufile;
|
const char *tofufile;
|
||||||
size_t metalen;
|
const char *proxy;
|
||||||
|
const char *meta;
|
||||||
|
const char *extra;
|
||||||
size_t reqlen;
|
size_t reqlen;
|
||||||
|
size_t reslen;
|
||||||
|
size_t metalen;
|
||||||
|
size_t extralen;
|
||||||
size_t keylen;
|
size_t keylen;
|
||||||
size_t certlen;
|
size_t certlen;
|
||||||
size_t index;
|
size_t index;
|
||||||
|
@ -55,6 +59,7 @@ struct gemini {
|
||||||
|
|
||||||
struct gemini *gemini_create(void);
|
struct gemini *gemini_create(void);
|
||||||
void gemini_init(struct gemini *);
|
void gemini_init(struct gemini *);
|
||||||
|
void gemini_envinit(struct gemini *);
|
||||||
void gemini_reset(struct gemini *);
|
void gemini_reset(struct gemini *);
|
||||||
int gemini_connect_query(struct gemini *, const char *,
|
int gemini_connect_query(struct gemini *, const char *,
|
||||||
const char *);
|
const char *);
|
||||||
|
|
Loading…
Reference in New Issue