Revert YAZ-830 fix (hack)
[yaz-moved-to-github.git] / src / tcpip.c
index 3ade82b..a8f03ba 100644 (file)
@@ -17,6 +17,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <signal.h>
+#include <yaz/base64.h>
 #if HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #endif
@@ -74,8 +75,6 @@
 static void tcpip_close(COMSTACK h);
 static int tcpip_put(COMSTACK h, char *buf, int size);
 static int tcpip_get(COMSTACK h, char **buf, int *bufsize);
-static int tcpip_put_connect(COMSTACK h, char *buf, int size);
-static int tcpip_get_connect(COMSTACK h, char **buf, int *bufsize);
 static int tcpip_connect(COMSTACK h, void *address);
 static int tcpip_more(COMSTACK h);
 static int tcpip_rcvconnect(COMSTACK h);
@@ -85,12 +84,6 @@ static int tcpip_listen(COMSTACK h, char *raddr, int *addrlen,
                  void *cd);
 static int tcpip_set_blocking(COMSTACK p, int blocking);
 
-#if HAVE_GNUTLS_H
-static int ssl_get(COMSTACK h, char **buf, int *bufsize);
-static int ssl_put(COMSTACK h, char *buf, int size);
-#endif
-
-
 #if HAVE_GETADDRINFO
 struct addrinfo *tcpip_getaddrinfo(const char *str, const char *port,
                                    int *ipv6_only);
@@ -251,13 +244,10 @@ COMSTACK tcpip_type(int s, int flags, int protocol, void *vp)
     return p;
 }
 
-COMSTACK yaz_tcpip_create2(int s, int flags, int protocol,
-                           const char *connect_host,
-                           const char *bind_host)
+static void connect_and_bind(COMSTACK p,
+                             const char *connect_host, const char *connect_auth,
+                             const char *bind_host)
 {
-    COMSTACK p = tcpip_type(s, flags, protocol, 0);
-    if (!p)
-        return 0;
     if (bind_host)
     {
         tcpip_state *sp = (tcpip_state *) p->cprivate;
@@ -274,17 +264,45 @@ COMSTACK yaz_tcpip_create2(int s, int flags, int protocol,
     if (connect_host)
     {
         tcpip_state *sp = (tcpip_state *) p->cprivate;
-        sp->connect_request_buf = (char *) xmalloc(strlen(connect_host) + 30);
-        sprintf(sp->connect_request_buf, "CONNECT %s HTTP/1.0\r\n\r\n",
-                connect_host);
+        char *cp;
+        sp->connect_request_buf = (char *) xmalloc(strlen(connect_host) + 130);
+        strcpy(sp->connect_request_buf, "CONNECT ");
+        strcat(sp->connect_request_buf, connect_host);
+        cp = strchr(sp->connect_request_buf, '/');
+        if (cp)
+            *cp = '\0';
+        strcat(sp->connect_request_buf, " HTTP/1.0\r\n");
+        if (connect_auth && strlen(connect_auth) < 40)
+        {
+            strcat(sp->connect_request_buf, "Proxy-Authorization: Basic ");
+            yaz_base64encode(connect_auth, sp->connect_request_buf +
+                             strlen(sp->connect_request_buf));
+            strcat(sp->connect_request_buf, "\r\n");
+        }
+        strcat(sp->connect_request_buf, "\r\n");
         sp->connect_request_len = strlen(sp->connect_request_buf);
-        p->f_put = tcpip_put_connect;
-        p->f_get = tcpip_get_connect;
-        sp->complete = cs_complete_auto_head; /* only want HTTP header */
     }
+}
+
+COMSTACK yaz_tcpip_create3(int s, int flags, int protocol,
+                           const char *connect_host,
+                           const char *connect_auth,
+                           const char *bind_host)
+{
+    COMSTACK p = tcpip_type(s, flags, protocol, 0);
+    if (!p)
+        return 0;
+    connect_and_bind(p, connect_host, 0, bind_host);
     return p;
 }
 
+COMSTACK yaz_tcpip_create2(int s, int flags, int protocol,
+                           const char *connect_host,
+                           const char *bind_host)
+{
+    return yaz_tcpip_create3(s, flags, protocol, connect_host, 0, bind_host);
+}
+
 COMSTACK yaz_tcpip_create(int s, int flags, int protocol,
                           const char *connect_host)
 {
@@ -311,8 +329,6 @@ COMSTACK ssl_type(int s, int flags, int protocol, void *vp)
     p = tcpip_type(s, flags, protocol, 0);
     if (!p)
         return 0;
-    p->f_get = ssl_get;
-    p->f_put = ssl_put;
     p->type = ssl_type;
     sp = (tcpip_state *) p->cprivate;
 
@@ -324,6 +340,18 @@ COMSTACK ssl_type(int s, int flags, int protocol, void *vp)
 #endif
 }
 
+COMSTACK yaz_ssl_create(int s, int flags, int protocol,
+                        const char *connect_host,
+                        const char *connect_auth,
+                        const char *bind_host)
+{
+    COMSTACK p = ssl_type(s, flags, protocol, 0);
+    if (!p)
+        return 0;
+    connect_and_bind(p, connect_host, connect_auth, bind_host);
+    return p;
+}
+
 #if HAVE_GNUTLS_H
 static int ssl_check_error(COMSTACK h, tcpip_state *sp, int res)
 {
@@ -772,6 +800,35 @@ int tcpip_rcvconnect(COMSTACK h)
         h->cerrno = CSOUTSTATE;
         return -1;
     }
+    if (sp->connect_request_buf)
+    {
+        int r;
+
+        sp->complete = cs_complete_auto_head;
+        if (sp->connect_request_len > 0)
+        {
+            r = tcpip_put(h, sp->connect_request_buf,
+                          sp->connect_request_len);
+            TRC(fprintf(stderr, "tcpip_put CONNECT r=%d\n", r));
+            h->event = CS_CONNECT; /* because tcpip_put sets it */
+            if (r) /* < 0 is error, 1 is in-complete */
+                return r;
+            TRC(fprintf(stderr, "tcpip_put CONNECT complete\n"));
+            TRC(fwrite(sp->connect_request_buf, 1, sp->connect_request_len, stderr));
+        }
+        sp->connect_request_len = 0;
+
+        r = tcpip_get(h, &sp->connect_response_buf, &sp->connect_response_len);
+        TRC(fprintf(stderr, "tcpip_get CONNECT r=%d\n", r));
+        if (r == 1)
+            return r;
+        if (r <= 0)
+            return -1;
+        TRC(fwrite(sp->connect_response_buf, 1, r, stderr));
+        xfree(sp->connect_request_buf);
+        sp->connect_request_buf = 0;
+        sp->complete = cs_complete_auto;
+    }
 #if HAVE_GNUTLS_H
     if (h->type == ssl_type && !sp->session)
     {
@@ -962,7 +1019,9 @@ COMSTACK tcpip_accept(COMSTACK h)
     TRC(fprintf(stderr, "tcpip_accept h=%p pid=%d\n", h, getpid()));
     if (h->state == CS_ST_INCON)
     {
+#if HAVE_GNUTLS_H
         tcpip_state *st = (tcpip_state *)h->cprivate;
+#endif
         tcpip_state *state = tcpip_state_create();
         cnew = (COMSTACK) xmalloc(sizeof(*cnew));
 
@@ -1106,55 +1165,76 @@ int tcpip_get(COMSTACK h, char **buf, int *bufsize)
                 h->cerrno = CSYSERR;
                 return -1;
             }
-#ifdef __sun__
-        yaz_set_errno( 0 );
-        /* unfortunatly, sun sometimes forgets to set errno in recv
-           when EWOULDBLOCK etc. would be required (res = -1) */
-#endif
-        res = recv(h->iofile, *buf + hasread, CS_TCPIP_BUFCHUNK, 0);
-        TRC(fprintf(stderr, "  recv res=%d, hasread=%d\n", res, hasread));
-        if (res < 0)
+#if HAVE_GNUTLS_H
+        if (sp->session)
         {
-            TRC(fprintf(stderr, "  recv errno=%d, (%s)\n", yaz_errno(),
-                      strerror(yaz_errno())));
-#ifdef WIN32
-            if (WSAGetLastError() == WSAEWOULDBLOCK)
+            res = gnutls_record_recv(sp->session, *buf + hasread,
+                                     CS_TCPIP_BUFCHUNK);
+            if (res == 0)
             {
-                h->io_pending = CS_WANT_READ;
-                break;
+                TRC(fprintf(stderr, "gnutls_record_recv returned 0\n"));
+                return 0;
             }
-            else
+            else if (res < 0)
             {
-                h->cerrno = CSYSERR;
+                if (ssl_check_error(h, sp, res))
+                    break;
                 return -1;
             }
+        }
+        else
+#endif
+        {
+#ifdef __sun__
+            yaz_set_errno( 0 );
+            /* unfortunatly, sun sometimes forgets to set errno in recv
+               when EWOULDBLOCK etc. would be required (res = -1) */
+#endif
+            res = recv(h->iofile, *buf + hasread, CS_TCPIP_BUFCHUNK, 0);
+            TRC(fprintf(stderr, "  recv res=%d, hasread=%d\n", res, hasread));
+            if (res < 0)
+            {
+                TRC(fprintf(stderr, "  recv errno=%d, (%s)\n", yaz_errno(),
+                            strerror(yaz_errno())));
+#ifdef WIN32
+                if (WSAGetLastError() == WSAEWOULDBLOCK)
+                {
+                    h->io_pending = CS_WANT_READ;
+                    break;
+                }
+                else
+                {
+                    h->cerrno = CSYSERR;
+                    return -1;
+                }
 #else
-            if (yaz_errno() == EWOULDBLOCK
+                if (yaz_errno() == EWOULDBLOCK
 #ifdef EAGAIN
 #if EAGAIN != EWOULDBLOCK
-                || yaz_errno() == EAGAIN
+                    || yaz_errno() == EAGAIN
 #endif
 #endif
-                || yaz_errno() == EINPROGRESS
+                    || yaz_errno() == EINPROGRESS
 #ifdef __sun__
-                || yaz_errno() == ENOENT /* Sun's sometimes set errno to this */
+                    || yaz_errno() == ENOENT /* Sun's sometimes set errno to this */
 #endif
-                )
-            {
-                h->io_pending = CS_WANT_READ;
-                break;
-            }
-            else if (yaz_errno() == 0)
-                continue;
-            else
-            {
-                h->cerrno = CSYSERR;
-                return -1;
-            }
+                    )
+                {
+                    h->io_pending = CS_WANT_READ;
+                    break;
+                }
+                else if (yaz_errno() == 0)
+                    continue;
+                else
+                {
+                    h->cerrno = CSYSERR;
+                    return -1;
+                }
 #endif
+            }
+            else if (!res)
+                return hasread;
         }
-        else if (!res)
-            return hasread;
         hasread += res;
         if (hasread > h->max_recv_bytes)
         {
@@ -1194,84 +1274,6 @@ int tcpip_get(COMSTACK h, char **buf, int *bufsize)
 }
 
 
-#if HAVE_GNUTLS_H
-/*
- * Return: -1 error, >1 good, len of buffer, ==1 incomplete buffer,
- * 0=connection closed.
- */
-int ssl_get(COMSTACK h, char **buf, int *bufsize)
-{
-    tcpip_state *sp = (tcpip_state *)h->cprivate;
-    char *tmpc;
-    int tmpi, berlen, rest, req, tomove;
-    int hasread = 0, res;
-
-    TRC(fprintf(stderr, "ssl_get: bufsize=%d\n", *bufsize));
-    if (sp->altlen) /* switch buffers */
-    {
-        TRC(fprintf(stderr, "  %d bytes in altbuf (%p)\n", sp->altlen,
-                    sp->altbuf));
-        tmpc = *buf;
-        tmpi = *bufsize;
-        *buf = sp->altbuf;
-        *bufsize = sp->altsize;
-        hasread = sp->altlen;
-        sp->altlen = 0;
-        sp->altbuf = tmpc;
-        sp->altsize = tmpi;
-    }
-    h->io_pending = 0;
-    while (!(berlen = (*sp->complete)(*buf, hasread)))
-    {
-        if (!*bufsize)
-        {
-            if (!(*buf = (char *)xmalloc(*bufsize = CS_TCPIP_BUFCHUNK)))
-                return -1;
-        }
-        else if (*bufsize - hasread < CS_TCPIP_BUFCHUNK)
-            if (!(*buf =(char *)xrealloc(*buf, *bufsize *= 2)))
-                return -1;
-        res = gnutls_record_recv(sp->session, *buf + hasread,
-                                 CS_TCPIP_BUFCHUNK);
-        if (res == 0)
-        {
-            TRC(fprintf(stderr, "gnutls_record_recv returned 0\n"));
-            return 0;
-        }
-        else if (res < 0)
-        {
-            if (ssl_check_error(h, sp, res))
-                break;
-            return -1;
-        }
-        hasread += res;
-    }
-    TRC (fprintf (stderr, "  Out of read loop with hasread=%d, berlen=%d\n",
-        hasread, berlen));
-    /* move surplus buffer (or everything if we didn't get a BER rec.) */
-    if (hasread > berlen)
-    {
-        tomove = req = hasread - berlen;
-        rest = tomove % CS_TCPIP_BUFCHUNK;
-        if (rest)
-            req += CS_TCPIP_BUFCHUNK - rest;
-        if (!sp->altbuf)
-        {
-            if (!(sp->altbuf = (char *)xmalloc(sp->altsize = req)))
-                return -1;
-        } else if (sp->altsize < req)
-            if (!(sp->altbuf =(char *)xrealloc(sp->altbuf, sp->altsize = req)))
-                return -1;
-        TRC(fprintf(stderr, "  Moving %d bytes to altbuf(%p)\n", tomove,
-                    sp->altbuf));
-        memcpy(sp->altbuf, *buf + berlen, sp->altlen = tomove);
-    }
-    if (berlen < CS_TCPIP_BUFCHUNK - 1)
-        *(*buf + berlen) = '\0';
-    return berlen ? berlen : 1;
-}
-#endif
-
 /*
  * Returns 1, 0 or -1
  * In nonblocking mode, you must call again with same buffer while
@@ -1297,38 +1299,60 @@ int tcpip_put(COMSTACK h, char *buf, int size)
     }
     while (state->towrite > state->written)
     {
-        if ((res =
-             send(h->iofile, buf + state->written, size -
-                  state->written,
+#if HAVE_GNUTLS_H
+        if (state->session)
+        {
+            res = gnutls_record_send(state->session, buf + state->written,
+                                     size - state->written);
+            if (res <= 0)
+            {
+                if (ssl_check_error(h, state, res))
+                    return 1;
+                return -1;
+            }
+        }
+        else
+#endif
+        {
+            if ((res =
+                 send(h->iofile, buf + state->written, size -
+                      state->written,
 #ifdef MSG_NOSIGNAL
-                  MSG_NOSIGNAL
+                      MSG_NOSIGNAL
 #else
-                  0
+                      0
 #endif
-                 )) < 0)
-        {
-            if (
+                     )) < 0)
+            {
+                if (
 #ifdef WIN32
-                WSAGetLastError() == WSAEWOULDBLOCK
+                    WSAGetLastError() == WSAEWOULDBLOCK
 #else
-                yaz_errno() == EWOULDBLOCK
+                    yaz_errno() == EWOULDBLOCK
 #ifdef EAGAIN
 #if EAGAIN != EWOULDBLOCK
-             || yaz_errno() == EAGAIN
+                    || yaz_errno() == EAGAIN
 #endif
 #endif
 #ifdef __sun__
-                || yaz_errno() == ENOENT /* Sun's sometimes set errno to this value! */
+                    || yaz_errno() == ENOENT /* Sun's sometimes set errno to this value! */
 #endif
-                || yaz_errno() == EINPROGRESS
+                    || yaz_errno() == EINPROGRESS
 #endif
-                )
-            {
-                TRC(fprintf(stderr, "  Flow control stop\n"));
-                h->io_pending = CS_WANT_WRITE;
-                return 1;
+                    )
+                {
+                    TRC(fprintf(stderr, "  Flow control stop\n"));
+                    h->io_pending = CS_WANT_WRITE;
+                    return 1;
+                }
+                if (h->flags & CS_FLAGS_BLOCKING)
+                {
+                    h->cerrno = CSYSERR;
+                    return -1;
+                }
+                else
+                    return cont_connect(h);
             }
-            return cont_connect(h);
         }
         state->written += res;
         TRC(fprintf(stderr, "  Wrote %d, written=%d, nbytes=%d\n",
@@ -1339,51 +1363,6 @@ int tcpip_put(COMSTACK h, char *buf, int size)
     return 0;
 }
 
-
-#if HAVE_GNUTLS_H
-/*
- * Returns 1, 0 or -1
- * In nonblocking mode, you must call again with same buffer while
- * return value is 1.
- */
-int ssl_put(COMSTACK h, char *buf, int size)
-{
-    int res;
-    struct tcpip_state *state = (struct tcpip_state *)h->cprivate;
-
-    TRC(fprintf(stderr, "ssl_put: size=%d\n", size));
-    h->io_pending = 0;
-    h->event = CS_DATA;
-    if (state->towrite < 0)
-    {
-        state->towrite = size;
-        state->written = 0;
-    }
-    else if (state->towrite != size)
-    {
-        h->cerrno = CSWRONGBUF;
-        return -1;
-    }
-    while (state->towrite > state->written)
-    {
-        res = gnutls_record_send(state->session, buf + state->written,
-                                 size - state->written);
-        if (res <= 0)
-        {
-            if (ssl_check_error(h, state, res))
-                return 1;
-            return -1;
-        }
-        state->written += res;
-        TRC(fprintf(stderr, "  Wrote %d, written=%d, nbytes=%d\n",
-                    res, state->written, size));
-    }
-    state->towrite = state->written = -1;
-    TRC(fprintf(stderr, "  Ok\n"));
-    return 0;
-}
-#endif
-
 void tcpip_close(COMSTACK h)
 {
     tcpip_state *sp = (struct tcpip_state *)h->cprivate;
@@ -1716,37 +1695,6 @@ int cs_get_peer_certificate_x509(COMSTACK cs, char **buf, int *len)
     return 0;
 }
 
-static int tcpip_put_connect(COMSTACK h, char *buf, int size)
-{
-    struct tcpip_state *state = (struct tcpip_state *)h->cprivate;
-
-    int r = tcpip_put(h, state->connect_request_buf,
-                      state->connect_request_len);
-    if (r == 0)
-    {
-        /* it's sent */
-        h->f_put = tcpip_put; /* switch to normal tcpip put */
-        r = tcpip_put(h, buf, size);
-    }
-    return r;
-}
-
-static int tcpip_get_connect(COMSTACK h, char **buf, int *bufsize)
-{
-    struct tcpip_state *state = (struct tcpip_state *)h->cprivate;
-    int r;
-
-    r = tcpip_get(h, &state->connect_response_buf,
-                  &state->connect_response_len);
-    if (r < 1)
-        return r;
-    /* got the connect response completely */
-    state->complete = cs_complete_auto; /* switch to normal tcpip get */
-    h->f_get = tcpip_get;
-    return tcpip_get(h, buf, bufsize);
-}
-
-
 /*
  * Local variables:
  * c-basic-offset: 4