aboutsummaryrefslogtreecommitdiffstats
path: root/istream.c
diff options
context:
space:
mode:
authorTatsuya Kinoshita <tats@vega.ocn.ne.jp>2011-05-04 07:05:14 +0000
committerTatsuya Kinoshita <tats@vega.ocn.ne.jp>2011-05-04 07:05:14 +0000
commit72f72d64a422d6628c4796f5c0bf2e508f134214 (patch)
tree0c9ea90cc53310832c977265521fb44db24a515e /istream.c
parentAdding upstream version 0.3 (diff)
downloadw3m-72f72d64a422d6628c4796f5c0bf2e508f134214.tar.gz
w3m-72f72d64a422d6628c4796f5c0bf2e508f134214.zip
Adding upstream version 0.5.1upstream/0.5.1
Diffstat (limited to 'istream.c')
-rw-r--r--istream.c723
1 files changed, 723 insertions, 0 deletions
diff --git a/istream.c b/istream.c
new file mode 100644
index 0000000..fe5847b
--- /dev/null
+++ b/istream.c
@@ -0,0 +1,723 @@
+/* $Id: istream.c,v 1.23 2003/10/20 16:41:56 ukai Exp $ */
+#include "fm.h"
+#include "myctype.h"
+#include "istream.h"
+#include <signal.h>
+#ifdef USE_SSL
+#include <openssl/x509v3.h>
+#endif
+
+#define uchar unsigned char
+
+#define STREAM_BUF_SIZE 8192
+#define SSL_BUF_SIZE 1536
+
+#define MUST_BE_UPDATED(bs) ((bs)->stream.cur==(bs)->stream.next)
+
+#define POP_CHAR(bs) ((bs)->iseos?'\0':(bs)->stream.buf[(bs)->stream.cur++])
+
+static void basic_close(int *handle);
+static int basic_read(int *handle, char *buf, int len);
+
+static void file_close(struct file_handle *handle);
+static int file_read(struct file_handle *handle, char *buf, int len);
+
+static int str_read(Str handle, char *buf, int len);
+
+#ifdef USE_SSL
+static void ssl_close(struct ssl_handle *handle);
+static int ssl_read(struct ssl_handle *handle, char *buf, int len);
+#endif
+
+static int ens_read(struct ens_handle *handle, char *buf, int len);
+static void ens_close(struct ens_handle *handle);
+
+static void
+do_update(BaseStream base)
+{
+ int len;
+ base->stream.cur = base->stream.next = 0;
+ len = base->read(base->handle, base->stream.buf, base->stream.size);
+ if (len <= 0)
+ base->iseos = TRUE;
+ else
+ base->stream.next += len;
+}
+
+static int
+buffer_read(StreamBuffer sb, char *obuf, int count)
+{
+ int len = sb->next - sb->cur;
+ if (len > 0) {
+ if (len > count)
+ len = count;
+ bcopy((const void *)&sb->buf[sb->cur], obuf, len);
+ sb->cur += len;
+ }
+ return len;
+}
+
+static void
+init_buffer(BaseStream base, char *buf, int bufsize)
+{
+ StreamBuffer sb = &base->stream;
+ sb->size = bufsize;
+ sb->cur = 0;
+ if (buf) {
+ sb->buf = (uchar *) buf;
+ sb->next = bufsize;
+ }
+ else {
+ sb->buf = NewAtom_N(uchar, bufsize);
+ sb->next = 0;
+ }
+ base->iseos = FALSE;
+}
+
+static void
+init_base_stream(BaseStream base, int bufsize)
+{
+ init_buffer(base, NULL, bufsize);
+}
+
+static void
+init_str_stream(BaseStream base, Str s)
+{
+ init_buffer(base, s->ptr, s->length);
+}
+
+InputStream
+newInputStream(int des)
+{
+ InputStream stream;
+ if (des < 0)
+ return NULL;
+ stream = New(union input_stream);
+ init_base_stream(&stream->base, STREAM_BUF_SIZE);
+ stream->base.type = IST_BASIC;
+ stream->base.handle = New(int);
+ *(int *)stream->base.handle = des;
+ stream->base.read = (int (*)())basic_read;
+ stream->base.close = (void (*)())basic_close;
+ return stream;
+}
+
+InputStream
+newFileStream(FILE * f, void (*closep) ())
+{
+ InputStream stream;
+ if (f == NULL)
+ return NULL;
+ stream = New(union input_stream);
+ init_base_stream(&stream->base, STREAM_BUF_SIZE);
+ stream->file.type = IST_FILE;
+ stream->file.handle = New(struct file_handle);
+ stream->file.handle->f = f;
+ if (closep)
+ stream->file.handle->close = closep;
+ else
+ stream->file.handle->close = (void (*)())fclose;
+ stream->file.read = (int (*)())file_read;
+ stream->file.close = (void (*)())file_close;
+ return stream;
+}
+
+InputStream
+newStrStream(Str s)
+{
+ InputStream stream;
+ if (s == NULL)
+ return NULL;
+ stream = New(union input_stream);
+ init_str_stream(&stream->base, s);
+ stream->str.type = IST_STR;
+ stream->str.handle = s;
+ stream->str.read = (int (*)())str_read;
+ stream->str.close = NULL;
+ return stream;
+}
+
+#ifdef USE_SSL
+InputStream
+newSSLStream(SSL * ssl, int sock)
+{
+ InputStream stream;
+ if (sock < 0)
+ return NULL;
+ stream = New(union input_stream);
+ init_base_stream(&stream->base, SSL_BUF_SIZE);
+ stream->ssl.type = IST_SSL;
+ stream->ssl.handle = New(struct ssl_handle);
+ stream->ssl.handle->ssl = ssl;
+ stream->ssl.handle->sock = sock;
+ stream->ssl.read = (int (*)())ssl_read;
+ stream->ssl.close = (void (*)())ssl_close;
+ return stream;
+}
+#endif
+
+InputStream
+newEncodedStream(InputStream is, char encoding)
+{
+ InputStream stream;
+ if (is == NULL || (encoding != ENC_QUOTE && encoding != ENC_BASE64 &&
+ encoding != ENC_UUENCODE))
+ return is;
+ stream = New(union input_stream);
+ init_base_stream(&stream->base, STREAM_BUF_SIZE);
+ stream->ens.type = IST_ENCODED;
+ stream->ens.handle = New(struct ens_handle);
+ stream->ens.handle->is = is;
+ stream->ens.handle->pos = 0;
+ stream->ens.handle->encoding = encoding;
+ stream->ens.handle->s = NULL;
+ stream->ens.read = (int (*)())ens_read;
+ stream->ens.close = (void (*)())ens_close;
+ return stream;
+}
+
+int
+ISclose(InputStream stream)
+{
+ MySignalHandler(*prevtrap) ();
+ if (stream == NULL || stream->base.close == NULL ||
+ stream->base.type & IST_UNCLOSE)
+ return -1;
+ prevtrap = mySignal(SIGINT, SIG_IGN);
+ stream->base.close(stream->base.handle);
+ mySignal(SIGINT, prevtrap);
+ return 0;
+}
+
+int
+ISgetc(InputStream stream)
+{
+ BaseStream base;
+ if (stream == NULL)
+ return '\0';
+ base = &stream->base;
+ if (!base->iseos && MUST_BE_UPDATED(base))
+ do_update(base);
+ return POP_CHAR(base);
+}
+
+int
+ISundogetc(InputStream stream)
+{
+ StreamBuffer sb;
+ if (stream == NULL)
+ return -1;
+ sb = &stream->base.stream;
+ if (sb->cur > 0) {
+ sb->cur--;
+ return 0;
+ }
+ return -1;
+}
+
+#define MARGIN_STR_SIZE 10
+Str
+StrISgets(InputStream stream)
+{
+ BaseStream base;
+ StreamBuffer sb;
+ Str s = NULL;
+ uchar *p;
+ int len;
+
+ if (stream == NULL)
+ return '\0';
+ base = &stream->base;
+ sb = &base->stream;
+
+ while (!base->iseos) {
+ if (MUST_BE_UPDATED(base)) {
+ do_update(base);
+ }
+ else {
+ if ((p = memchr(&sb->buf[sb->cur], '\n', sb->next - sb->cur))) {
+ len = p - &sb->buf[sb->cur] + 1;
+ if (s == NULL)
+ s = Strnew_size(len);
+ Strcat_charp_n(s, (char *)&sb->buf[sb->cur], len);
+ sb->cur += len;
+ return s;
+ }
+ else {
+ if (s == NULL)
+ s = Strnew_size(sb->next - sb->cur + MARGIN_STR_SIZE);
+ Strcat_charp_n(s, (char *)&sb->buf[sb->cur],
+ sb->next - sb->cur);
+ sb->cur = sb->next;
+ }
+ }
+ }
+
+ if (s == NULL)
+ return Strnew();
+ return s;
+}
+
+Str
+StrmyISgets(InputStream stream)
+{
+ BaseStream base;
+ StreamBuffer sb;
+ Str s = NULL;
+ int i, len;
+
+ if (stream == NULL)
+ return '\0';
+ base = &stream->base;
+ sb = &base->stream;
+
+ while (!base->iseos) {
+ if (MUST_BE_UPDATED(base)) {
+ do_update(base);
+ }
+ else {
+ if (s && Strlastchar(s) == '\r') {
+ if (sb->buf[sb->cur] == '\n')
+ Strcat_char(s, (char)sb->buf[sb->cur++]);
+ return s;
+ }
+ for (i = sb->cur;
+ i < sb->next && sb->buf[i] != '\n' && sb->buf[i] != '\r';
+ i++) ;
+ if (i < sb->next) {
+ len = i - sb->cur + 1;
+ if (s == NULL)
+ s = Strnew_size(len + MARGIN_STR_SIZE);
+ Strcat_charp_n(s, (char *)&sb->buf[sb->cur], len);
+ sb->cur = i + 1;
+ if (sb->buf[i] == '\n')
+ return s;
+ }
+ else {
+ if (s == NULL)
+ s = Strnew_size(sb->next - sb->cur + MARGIN_STR_SIZE);
+ Strcat_charp_n(s, (char *)&sb->buf[sb->cur],
+ sb->next - sb->cur);
+ sb->cur = sb->next;
+ }
+ }
+ }
+
+ if (s == NULL)
+ return Strnew();
+ return s;
+}
+
+int
+ISread(InputStream stream, Str buf, int count)
+{
+ int rest, len;
+ BaseStream base;
+
+ if (stream == NULL || (base = &stream->base)->iseos)
+ return 0;
+
+ len = buffer_read(&base->stream, buf->ptr, count);
+ rest = count - len;
+ if (MUST_BE_UPDATED(base)) {
+ len = base->read(base->handle, &buf->ptr[len], rest);
+ if (len <= 0) {
+ base->iseos = TRUE;
+ len = 0;
+ }
+ rest -= len;
+ }
+ Strtruncate(buf, count - rest);
+ if (buf->length > 0)
+ return 1;
+ return 0;
+}
+
+int
+ISfileno(InputStream stream)
+{
+ if (stream == NULL)
+ return -1;
+ switch (IStype(stream) & ~IST_UNCLOSE) {
+ case IST_BASIC:
+ return *(int *)stream->base.handle;
+ case IST_FILE:
+ return fileno(stream->file.handle->f);
+#ifdef USE_SSL
+ case IST_SSL:
+ return stream->ssl.handle->sock;
+#endif
+ case IST_ENCODED:
+ return ISfileno(stream->ens.handle->is);
+ default:
+ return -1;
+ }
+}
+
+int
+ISeos(InputStream stream)
+{
+ BaseStream base = &stream->base;
+ if (!base->iseos && MUST_BE_UPDATED(base))
+ do_update(base);
+ return base->iseos;
+}
+
+#ifdef USE_SSL
+static Str accept_this_site;
+
+void
+ssl_accept_this_site(char *hostname)
+{
+ if (hostname)
+ accept_this_site = Strnew_charp(hostname);
+ else
+ accept_this_site = NULL;
+}
+
+static int
+ssl_match_cert_ident(char *ident, int ilen, char *hostname)
+{
+ /* RFC2818 3.1. Server Identity
+ * Names may contain the wildcard
+ * character * which is considered to match any single domain name
+ * component or component fragment. E.g., *.a.com matches foo.a.com but
+ * not bar.foo.a.com. f*.com matches foo.com but not bar.com.
+ */
+ int hlen = strlen(hostname);
+ int i, c;
+
+ /* Is this an exact match? */
+ if ((ilen == hlen) && strncasecmp(ident, hostname, hlen) == 0)
+ return TRUE;
+
+ for (i = 0; i < ilen; i++) {
+ if (ident[i] == '*' && ident[i + 1] == '.') {
+ while ((c = *hostname++) != '\0')
+ if (c == '.')
+ break;
+ i++;
+ }
+ else {
+ if (ident[i] != *hostname++)
+ return FALSE;
+ }
+ }
+ return *hostname == '\0';
+}
+
+static Str
+ssl_check_cert_ident(X509 * x, char *hostname)
+{
+ int i;
+ Str ret = NULL;
+ int match_ident = FALSE;
+ /*
+ * All we need to do here is check that the CN matches.
+ *
+ * From RFC2818 3.1 Server Identity:
+ * If a subjectAltName extension of type dNSName is present, that MUST
+ * be used as the identity. Otherwise, the (most specific) Common Name
+ * field in the Subject field of the certificate MUST be used. Although
+ * the use of the Common Name is existing practice, it is deprecated and
+ * Certification Authorities are encouraged to use the dNSName instead.
+ */
+ i = X509_get_ext_by_NID(x, NID_subject_alt_name, -1);
+ if (i >= 0) {
+ X509_EXTENSION *ex;
+ STACK_OF(GENERAL_NAME) * alt;
+
+ ex = X509_get_ext(x, i);
+ alt = X509V3_EXT_d2i(ex);
+ if (alt) {
+ int n;
+ GENERAL_NAME *gn;
+ X509V3_EXT_METHOD *method;
+ Str seen_dnsname = NULL;
+
+ n = sk_GENERAL_NAME_num(alt);
+ for (i = 0; i < n; i++) {
+ gn = sk_GENERAL_NAME_value(alt, i);
+ if (gn->type == GEN_DNS) {
+ char *sn = ASN1_STRING_data(gn->d.ia5);
+ int sl = ASN1_STRING_length(gn->d.ia5);
+
+ if (!seen_dnsname)
+ seen_dnsname = Strnew();
+ Strcat_m_charp(seen_dnsname, sn, " ", NULL);
+ if (ssl_match_cert_ident(sn, sl, hostname))
+ break;
+ }
+ }
+ method = X509V3_EXT_get(ex);
+ sk_GENERAL_NAME_free(alt);
+ if (i < n) /* Found a match */
+ match_ident = TRUE;
+ else if (seen_dnsname)
+ /* FIXME: gettextize? */
+ ret = Sprintf("Bad cert ident from %s: dNSName=%s", hostname,
+ seen_dnsname->ptr);
+ }
+ }
+
+ if (match_ident == FALSE && ret == NULL) {
+ X509_NAME *xn;
+ char buf[2048];
+
+ xn = X509_get_subject_name(x);
+
+ if (X509_NAME_get_text_by_NID(xn, NID_commonName,
+ buf, sizeof(buf)) == -1)
+ /* FIXME: gettextize? */
+ ret = Strnew_charp("Unable to get common name from peer cert");
+ else if (!ssl_match_cert_ident(buf, strlen(buf), hostname))
+ /* FIXME: gettextize? */
+ ret = Sprintf("Bad cert ident %s from %s", buf, hostname);
+ else
+ match_ident = TRUE;
+ }
+ return ret;
+}
+
+Str
+ssl_get_certificate(SSL * ssl, char *hostname)
+{
+ BIO *bp;
+ X509 *x;
+ X509_NAME *xn;
+ char *p;
+ int len;
+ Str s;
+ char buf[2048];
+ Str amsg = NULL;
+ Str emsg;
+ char *ans;
+
+ if (ssl == NULL)
+ return NULL;
+ x = SSL_get_peer_certificate(ssl);
+ if (x == NULL) {
+ if (accept_this_site
+ && strcasecmp(accept_this_site->ptr, hostname) == 0)
+ ans = "y";
+ else {
+ /* FIXME: gettextize? */
+ emsg = Strnew_charp("No SSL peer certificate: accept? (y/n)");
+ ans = inputAnswer(emsg->ptr);
+ }
+ if (ans && TOLOWER(*ans) == 'y')
+ /* FIXME: gettextize? */
+ amsg = Strnew_charp
+ ("Accept SSL session without any peer certificate");
+ else {
+ /* FIXME: gettextize? */
+ char *e = "This SSL session was rejected "
+ "to prevent security violation: no peer certificate";
+ disp_err_message(e, FALSE);
+ free_ssl_ctx();
+ return NULL;
+ }
+ if (amsg)
+ disp_err_message(amsg->ptr, FALSE);
+ ssl_accept_this_site(hostname);
+ /* FIXME: gettextize? */
+ s = amsg ? amsg : Strnew_charp("valid certificate");
+ return s;
+ }
+#ifdef USE_SSL_VERIFY
+ /* check the cert chain.
+ * The chain length is automatically checked by OpenSSL when we
+ * set the verify depth in the ctx.
+ */
+ if (ssl_verify_server) {
+ long verr;
+ if ((verr = SSL_get_verify_result(ssl))
+ != X509_V_OK) {
+ const char *em = X509_verify_cert_error_string(verr);
+ if (accept_this_site
+ && strcasecmp(accept_this_site->ptr, hostname) == 0)
+ ans = "y";
+ else {
+ /* FIXME: gettextize? */
+ emsg = Sprintf("%s: accept? (y/n)", em);
+ ans = inputAnswer(emsg->ptr);
+ }
+ if (ans && TOLOWER(*ans) == 'y') {
+ /* FIXME: gettextize? */
+ amsg = Sprintf("Accept unsecure SSL session: "
+ "unverified: %s", em);
+ }
+ else {
+ /* FIXME: gettextize? */
+ char *e =
+ Sprintf("This SSL session was rejected: %s", em)->ptr;
+ disp_err_message(e, FALSE);
+ free_ssl_ctx();
+ return NULL;
+ }
+ }
+ }
+#endif
+ emsg = ssl_check_cert_ident(x, hostname);
+ if (emsg != NULL) {
+ if (accept_this_site
+ && strcasecmp(accept_this_site->ptr, hostname) == 0)
+ ans = "y";
+ else {
+ Str ep = Strdup(emsg);
+ if (ep->length > COLS - 16)
+ Strshrink(ep, ep->length - (COLS - 16));
+ Strcat_charp(ep, ": accept? (y/n)");
+ ans = inputAnswer(ep->ptr);
+ }
+ if (ans && TOLOWER(*ans) == 'y') {
+ /* FIXME: gettextize? */
+ amsg = Strnew_charp("Accept unsecure SSL session:");
+ Strcat(amsg, emsg);
+ }
+ else {
+ /* FIXME: gettextize? */
+ char *e = "This SSL session was rejected "
+ "to prevent security violation";
+ disp_err_message(e, FALSE);
+ free_ssl_ctx();
+ return NULL;
+ }
+ }
+ if (amsg)
+ disp_err_message(amsg->ptr, FALSE);
+ ssl_accept_this_site(hostname);
+ /* FIXME: gettextize? */
+ s = amsg ? amsg : Strnew_charp("valid certificate");
+ Strcat_charp(s, "\n");
+ xn = X509_get_subject_name(x);
+ if (X509_NAME_get_text_by_NID(xn, NID_commonName, buf, sizeof(buf)) == -1)
+ Strcat_charp(s, " subject=<unknown>");
+ else
+ Strcat_m_charp(s, " subject=", buf, NULL);
+ xn = X509_get_issuer_name(x);
+ if (X509_NAME_get_text_by_NID(xn, NID_commonName, buf, sizeof(buf)) == -1)
+ Strcat_charp(s, ": issuer=<unknown>");
+ else
+ Strcat_m_charp(s, ": issuer=", buf, NULL);
+ Strcat_charp(s, "\n\n");
+
+ bp = BIO_new(BIO_s_mem());
+ X509_print(bp, x);
+ len = (int)BIO_ctrl(bp, BIO_CTRL_INFO, 0, (char *)&p);
+ Strcat_charp_n(s, p, len);
+ BIO_free_all(bp);
+ X509_free(x);
+ return s;
+}
+#endif
+
+/* Raw level input stream functions */
+
+static void
+basic_close(int *handle)
+{
+ close(*(int *)handle);
+}
+
+static int
+basic_read(int *handle, char *buf, int len)
+{
+ return read(*(int *)handle, buf, len);
+}
+
+static void
+file_close(struct file_handle *handle)
+{
+ handle->close(handle->f);
+}
+
+static int
+file_read(struct file_handle *handle, char *buf, int len)
+{
+ return fread(buf, 1, len, handle->f);
+}
+
+static int
+str_read(Str handle, char *buf, int len)
+{
+ return 0;
+}
+
+#ifdef USE_SSL
+static void
+ssl_close(struct ssl_handle *handle)
+{
+ close(handle->sock);
+ if (handle->ssl)
+ SSL_free(handle->ssl);
+}
+
+static int
+ssl_read(struct ssl_handle *handle, char *buf, int len)
+{
+ int status;
+ if (handle->ssl) {
+#ifdef USE_SSL_VERIFY
+ for (;;) {
+ status = SSL_read(handle->ssl, buf, len);
+ if (status > 0)
+ break;
+ switch (SSL_get_error(handle->ssl, status)) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE: /* reads can trigger write errors; see SSL_get_error(3) */
+ continue;
+ default:
+ break;
+ }
+ break;
+ }
+#else /* if !defined(USE_SSL_VERIFY) */
+ status = SSL_read(handle->ssl, buf, len);
+#endif /* !defined(USE_SSL_VERIFY) */
+ }
+ else
+ status = read(handle->sock, buf, len);
+ return status;
+}
+#endif /* USE_SSL */
+
+static void
+ens_close(struct ens_handle *handle)
+{
+ ISclose(handle->is);
+}
+
+static int
+ens_read(struct ens_handle *handle, char *buf, int len)
+{
+ if (handle->s == NULL || handle->pos == handle->s->length) {
+ char *p;
+ handle->s = StrmyISgets(handle->is);
+ if (handle->s->length == 0)
+ return 0;
+ cleanup_line(handle->s, PAGER_MODE);
+ if (handle->encoding == ENC_BASE64)
+ Strchop(handle->s);
+ else if (handle->encoding == ENC_UUENCODE) {
+ if (!strncmp(handle->s->ptr, "begin", 5))
+ handle->s = StrmyISgets(handle->is);
+ Strchop(handle->s);
+ }
+ p = handle->s->ptr;
+ if (handle->encoding == ENC_QUOTE)
+ handle->s = decodeQP(&p);
+ else if (handle->encoding == ENC_BASE64)
+ handle->s = decodeB(&p);
+ else if (handle->encoding == ENC_UUENCODE)
+ handle->s = decodeU(&p);
+ handle->pos = 0;
+ }
+
+ if (len > handle->s->length - handle->pos)
+ len = handle->s->length - handle->pos;
+
+ bcopy(&handle->s->ptr[handle->pos], buf, len);
+ handle->pos += len;
+ return len;
+}