Merge remote-tracking branch 'upstream/master' into httpproxy
authorChristian Plattner <ccpp@gmx.at>
Thu, 22 Jan 2015 13:32:15 +0000 (14:32 +0100)
committerChristian Plattner <ccpp@gmx.at>
Thu, 22 Jan 2015 13:33:17 +0000 (14:33 +0100)
Conflicts:
libfreerdp/core/settings.c
libfreerdp/core/tcp.c
libfreerdp/core/transport.c

1  2 
client/common/cmdline.c
include/freerdp/settings.h
libfreerdp/common/settings.c
libfreerdp/core/CMakeLists.txt
libfreerdp/core/proxy.c
libfreerdp/core/settings.c
libfreerdp/core/tcp.c
libfreerdp/core/transport.c

Simple merge
Simple merge
@@@ -933,16 -882,9 +882,13 @@@ BOOL freerdp_get_param_bool(rdpSettings
  
                case FreeRDP_GatewayBypassLocal:
                        return settings->GatewayBypassLocal;
-                       break;
  
 +              case FreeRDP_HTTPProxyEnabled:
 +                      return settings->HTTPProxyEnabled;
 +                      break;
 +
                case FreeRDP_RemoteApplicationMode:
                        return settings->RemoteApplicationMode;
-                       break;
  
                case FreeRDP_DisableRemoteAppCapsCheck:
                        return settings->DisableRemoteAppCapsCheck;
Simple merge
index 7b43f5f,0000000..6bde94a
mode 100644,000000..100644
--- /dev/null
@@@ -1,139 -1,0 +1,139 @@@
-  * Copyright 2014 Christian Plattner <ccpp@gmx.at>
 +/**
 + * FreeRDP: A Remote Desktop Protocol Implementation
 + * HTTP Proxy support
 + *
-                       "CONNECT %s:%d HTTP/1.1" CRLF CRLF
++ * Copyright 2015 Christian Plattner <ccpp@gmx.at>
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +/* Probably this is more portable */
 +#define TRIO_REPLACE_STDIO
 +
 +#include "proxy.h"
 +#include "freerdp/settings.h"
 +#include "tcp.h"
 +
 +#include "winpr/libwinpr/utils/trio/trio.h"   /* asprintf */
 +#include "winpr/environment.h"        /* For GetEnvironmentVariableA */
 +
 +#define CRLF "\r\n"
 +
 +void http_proxy_read_environment(rdpSettings *settings, char *envname)
 +{
 +      char env[256];
 +      DWORD envlen;
 +      char *hostname, *pport;
 +
 +      envlen = GetEnvironmentVariableA(envname, env, sizeof(env));
 +      if(!envlen)
 +              return;
 +
 +      if (strncmp(env, "http://", 7)) {
 +              fprintf(stderr, "Proxy url must have scheme http. Ignoring.\n");
 +              return;
 +      }
 +
 +      settings->HTTPProxyEnabled = TRUE;
 +
 +      hostname = env + 7;
 +      pport = strchr(hostname, ':');
 +      if (pport) {
 +              *pport = '\0';
 +              settings->HTTPProxyPort = atoi(pport+1);
 +      }
 +      else {
 +              /* The default is 80. Also for Proxys. */
 +              settings->HTTPProxyPort = 80;
 +
 +              pport = strchr(hostname, '/');
 +              if(pport)
 +                      *pport = '\0';
 +      }
 +
 +      freerdp_set_param_string(settings, FreeRDP_HTTPProxyHostname, hostname);
 +}
 +
 +BOOL http_proxy_connect(BIO* bufferedBio, const char* hostname, UINT16 port)
 +{
 +      int status;
 +      char *send_buf, recv_buf[512], *eol;
 +      int resultsize;
 +      int send_length;
 +
 +      send_length = trio_asprintf(&send_buf,
++                      "CONNECT %s:%d HTTP/1.1" CRLF
 +                      "Host: %s:%d" CRLF CRLF,
 +                      hostname, port,
 +                      hostname, port);
 +
 +      if (send_length <= 0) {
 +              fprintf(stderr, "HTTP proxy: failed to allocate memory\n");
 +              return FALSE;
 +      }
 +
 +      status = BIO_write(bufferedBio, send_buf, send_length);
 +      free(send_buf);
 +      send_buf = NULL;
 +
 +      if (status != send_length) {
 +              fprintf(stderr, "HTTP proxy: failed to write CONNECT request\n");
 +              return FALSE;
 +      }
 +
 +      /* Read result until CR-LF-CR-LF.
 +       * Keep recv_buf a null-terminated string. */
 +
 +      memset(recv_buf, '\0', sizeof(recv_buf));
 +      resultsize = 0;
 +      while ( strstr(recv_buf, "\r\n\r\n") == NULL ) {
 +              if (resultsize >= sizeof(recv_buf)-1) {
 +                      fprintf(stderr, "HTTP Reply headers too long.\n");
 +                      return FALSE;
 +              }
 +
 +              status = BIO_read(bufferedBio, (BYTE*)recv_buf + resultsize, sizeof(recv_buf)-resultsize-1);
 +              if (status < 0) {
 +                      /* Error? */
 +                      return FALSE;
 +              }
 +              else if (status == 0) {
 +                      /* Error? */
 +                      fprintf(stderr, "BIO_read() returned zero\n");
 +                      return FALSE;
 +              }
 +              resultsize += status;
 +      }
 +
 +      /* Extract HTTP status line */
 +      eol = strchr(recv_buf, '\r');
 +      if (!eol) {
 +              /* should never happen */
 +              return FALSE;
 +      }
 +
 +      *eol = '\0';
 +
 +      fprintf(stderr, "HTTP proxy: %s\n", recv_buf);
 +
 +      if (strlen(recv_buf) < 12) {
 +              return FALSE;
 +      }
 +
 +      recv_buf[7] = 'X';
 +      if (strncmp(recv_buf, "HTTP/1.X 200", 12))
 +              return FALSE;
 +      
 +      return TRUE;
 +}
 +
Simple merge
  #include <winpr/stream.h>
  
  #include "tcp.h"
 +#include "proxy.h"
  
+ #define TAG FREERDP_TAG("core")
+ /* Simple Socket BIO */
+ static int transport_bio_simple_new(BIO* bio);
+ static int transport_bio_simple_free(BIO* bio);
+ long transport_bio_simple_callback(BIO* bio, int mode, const char* argp, int argi, long argl, long ret)
+ {
+       return 1;
+ }
+ static int transport_bio_simple_write(BIO* bio, const char* buf, int size)
+ {
+       int error;
+       int status = 0;
+       if (!buf)
+               return 0;
+       BIO_clear_flags(bio, BIO_FLAGS_WRITE);
+       status = _send((SOCKET) bio->num, buf, size, 0);
+       if (status <= 0)
+       {
+               error = WSAGetLastError();
+               if ((error == WSAEWOULDBLOCK) || (error == WSAEINTR) ||
+                       (error == WSAEINPROGRESS) || (error == WSAEALREADY))
+               {
+                       BIO_set_flags(bio, (BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY));
+               }
+               else
+               {
+                       BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
+               }
+       }
+       return status;
+ }
+ static int transport_bio_simple_read(BIO* bio, char* buf, int size)
+ {
+       int error;
+       int status = 0;
+       if (!buf)
+               return 0;
+       BIO_clear_flags(bio, BIO_FLAGS_READ);
+       status = _recv((SOCKET) bio->num, buf, size, 0);
+       if (status > 0)
+               return status;
+       if (status == 0)
+       {
+               BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
+               return 0;
+       }
+       error = WSAGetLastError();
+       if ((error == WSAEWOULDBLOCK) || (error == WSAEINTR) ||
+               (error == WSAEINPROGRESS) || (error == WSAEALREADY))
+       {
+               BIO_set_flags(bio, (BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY));
+       }
+       else
+       {
+               BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
+       }
+       return -1;
+ }
+ static int transport_bio_simple_puts(BIO* bio, const char* str)
+ {
+       return 1;
+ }
+ static int transport_bio_simple_gets(BIO* bio, char* str, int size)
+ {
+       return 1;
+ }
+ static long transport_bio_simple_ctrl(BIO* bio, int cmd, long arg1, void* arg2)
+ {
+       int status = -1;
+       switch (cmd)
+       {
+               case BIO_C_SET_FD:
+                       if (arg2)
+                       {
+                               transport_bio_simple_free(bio);
+                               bio->flags = BIO_FLAGS_SHOULD_RETRY;
+                               bio->num = *((int*) arg2);
+                               bio->shutdown = (int) arg1;
+                               bio->init = 1;
+                               status = 1;
+                       }
+                       break;
+               case BIO_C_GET_FD:
+                       if (bio->init)
+                       {
+                               if (arg2)
+                                       *((int*) arg2) = bio->num;
+                               status = bio->num;
+                       }
+                       break;
+               case BIO_CTRL_GET_CLOSE:
+                       status = bio->shutdown;
+                       break;
+               case BIO_CTRL_SET_CLOSE:
+                       bio->shutdown = (int) arg1;
+                       status = 1;
+                       break;
+               case BIO_CTRL_DUP:
+                       status = 1;
+                       break;
+               case BIO_CTRL_FLUSH:
+                       status = 1;
+                       break;
+               default:
+                       status = 0;
+                       break;
+       }
+       return status;
+ }
+ static int transport_bio_simple_new(BIO* bio)
+ {
+       bio->init = 0;
+       bio->num = 0;
+       bio->ptr = NULL;
+       bio->flags = BIO_FLAGS_SHOULD_RETRY;
+       return 1;
+ }
+ static int transport_bio_simple_free(BIO* bio)
+ {
+       if (!bio)
+               return 0;
+       if (bio->shutdown)
+       {
+               if (bio->init)
+                       closesocket((SOCKET) bio->num);
+               bio->init = 0;
+               bio->flags = 0;
+       }
+       return 1;
+ }
+ static BIO_METHOD transport_bio_simple_socket_methods =
+ {
+       BIO_TYPE_SIMPLE,
+       "SimpleSocket",
+       transport_bio_simple_write,
+       transport_bio_simple_read,
+       transport_bio_simple_puts,
+       transport_bio_simple_gets,
+       transport_bio_simple_ctrl,
+       transport_bio_simple_new,
+       transport_bio_simple_free,
+       NULL,
+ };
+ BIO_METHOD* BIO_s_simple_socket(void)
+ {
+       return &transport_bio_simple_socket_methods;
+ }
+ /* Buffered Socket BIO */
  long transport_bio_buffered_callback(BIO* bio, int mode, const char* argp, int argi, long argl, long ret)
  {
        return 1;
@@@ -285,11 -499,313 +500,316 @@@ void freerdp_tcp_get_mac_address(rdpTcp
                mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); */
  }
  
- BOOL tcp_connect(rdpTcp* tcp, const char* hostname, int port)
+ int uds_connect(const char* path)
+ {
+ #ifndef _WIN32
+       int status;
+       int sockfd;
+       struct sockaddr_un addr;
+       sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (sockfd == -1)
+       {
+               WLog_ERR(TAG, "socket");
+               return -1;
+       }
+       addr.sun_family = AF_UNIX;
+       strncpy(addr.sun_path, path, sizeof(addr.sun_path));
+       status = connect(sockfd, (struct sockaddr *) &addr, sizeof(addr));
+       if (status < 0)
+       {
+               WLog_ERR(TAG, "connect");
+               close(sockfd);
+               return -1;
+       }
+       return sockfd;
+ #else /* ifndef _WIN32 */
+       return -1;
+ #endif
+ }
+ BOOL freerdp_tcp_resolve_hostname(const char* hostname)
+ {
+       int status;
+       struct addrinfo hints = { 0 };
+       struct addrinfo* result = NULL;
+       hints.ai_family = AF_UNSPEC;
+       hints.ai_socktype = SOCK_STREAM;
+       status = getaddrinfo(hostname, NULL, &hints, &result);
+       if (status)
+               return FALSE;
+       freeaddrinfo(result);
+       return TRUE;
+ }
+ BOOL freerdp_tcp_connect_timeout(int sockfd, struct sockaddr* addr, socklen_t addrlen, int timeout)
+ {
+       int status;
+ #ifndef _WIN32
+       int flags;
+       fd_set cfds;
+       socklen_t optlen;
+       struct timeval tv;
+       /* set socket in non-blocking mode */
+       flags = fcntl(sockfd, F_GETFL);
+       if (flags < 0)
+               return FALSE;
+       fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
+       /* non-blocking tcp connect */
+       status = connect(sockfd, addr, addrlen);
+       if (status >= 0)
+               return TRUE; /* connection success */
+       if (errno != EINPROGRESS)
+               return FALSE;
+       FD_ZERO(&cfds);
+       FD_SET(sockfd, &cfds);
+       tv.tv_sec = timeout;
+       tv.tv_usec = 0;
+       status = _select(sockfd + 1, NULL, &cfds, NULL, &tv);
+       if (status != 1)
+               return FALSE; /* connection timeout or error */
+       status = 0;
+       optlen = sizeof(status);
+       if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*) &status, &optlen) < 0)
+               return FALSE;
+       if (status != 0)
+               return FALSE;
+       /* set socket in blocking mode */
+       flags = fcntl(sockfd, F_GETFL);
+       if (flags < 0)
+               return FALSE;
+       fcntl(sockfd, F_SETFL, flags & ~O_NONBLOCK);
+ #else
+       status = connect(sockfd, addr, addrlen);
+       if (status >= 0)
+               return TRUE;
+       return FALSE;
+ #endif
+       return TRUE;
+ }
+ #ifndef _WIN32
+ int freerdp_tcp_connect_multi(char** hostnames, int count, int port, int timeout)
+ {
+       int index;
+       int sindex;
+       int status;
+       int flags;
+       int maxfds;
+       fd_set cfds;
+       int sockfd;
+       int* sockfds;
+       char port_str[16];
+       socklen_t optlen;
+       struct timeval tv;
+       struct addrinfo hints;
+       struct addrinfo* addr;
+       struct addrinfo* result;
+       struct addrinfo** addrs;
+       struct addrinfo** results;
+       sindex = -1;
+       sprintf_s(port_str, sizeof(port_str) - 1, "%u", port);
+       sockfds = (int*) calloc(count, sizeof(int));
+       addrs = (struct addrinfo**) calloc(count, sizeof(struct addrinfo*));
+       results = (struct addrinfo**) calloc(count, sizeof(struct addrinfo*));
+       for (index = 0; index < count; index++)
+       {
+               ZeroMemory(&hints, sizeof(hints));
+               hints.ai_family = AF_UNSPEC;
+               hints.ai_socktype = SOCK_STREAM;
+               status = getaddrinfo(hostnames[index], port_str, &hints, &result);
+               if (status)
+               {
+                       continue;
+               }
+               addr = result;
+               if ((addr->ai_family == AF_INET6) && (addr->ai_next != 0))
+               {
+                       while ((addr = addr->ai_next))
+                       {
+                               if (addr->ai_family == AF_INET)
+                                       break;
+                       }
+                       if (!addr)
+                               addr = result;
+               }
+               sockfds[index] = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
+               if (sockfds[index] < 0)
+               {
+                       freeaddrinfo(result);
+                       sockfds[index] = 0;
+                       continue;
+               }
+               addrs[index] = addr;
+               results[index] = result;
+       }
+       maxfds = 0;
+       FD_ZERO(&cfds);
+       for (index = 0; index < count; index++)
+       {
+               if (!sockfds[index])
+                       continue;
+               sockfd = sockfds[index];
+               addr = addrs[index];
+               /* set socket in non-blocking mode */
+               flags = fcntl(sockfd, F_GETFL);
+               if (flags < 0)
+               {
+                       sockfds[index] = 0;
+                       continue;
+               }
+               fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
+               /* non-blocking tcp connect */
+               status = connect(sockfd, addr->ai_addr, addr->ai_addrlen);
+               if (status >= 0)
+               {
+                       /* connection success */
+                       break;
+               }
+               if (errno != EINPROGRESS)
+               {
+                       sockfds[index] = 0;
+                       continue;
+               }
+               FD_SET(sockfd, &cfds);
+               if (sockfd > maxfds)
+                       maxfds = sockfd;
+       }
+       tv.tv_sec = timeout;
+       tv.tv_usec = 0;
+       status = _select(maxfds + 1, NULL, &cfds, NULL, &tv);
+       for (index = 0; index < count; index++)
+       {
+               if (!sockfds[index])
+                       continue;
+               sockfd = sockfds[index];
+               if (FD_ISSET(sockfd, &cfds))
+               {
+                       status = 0;
+                       optlen = sizeof(status);
+                       if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*) &status, &optlen) < 0)
+                       {
+                               sockfds[index] = 0;
+                               continue;
+                       }
+                       if (status != 0)
+                       {
+                               sockfds[index] = 0;
+                               continue;
+                       }
+                       /* set socket in blocking mode */
+                       flags = fcntl(sockfd, F_GETFL);
+                       if (flags < 0)
+                       {
+                               sockfds[index] = 0;
+                               continue;
+                       }
+                       fcntl(sockfd, F_SETFL, flags & ~O_NONBLOCK);
+                       sindex = index;
+                       break;
+               }
+       }
+       if (sindex >= 0)
+       {
+               sockfd = sockfds[sindex];
+       }
+       for (index = 0; index < count; index++)
+       {
+               if (results[index])
+                       freeaddrinfo(results[index]);
+       }
+       free(addrs);
+       free(results);
+       free(sockfds);
+       return sockfd;
+ }
+ #endif
+ BOOL freerdp_tcp_connect(rdpTcp* tcp, const char* hostname, int port, int timeout)
  {
+       int status;
        UINT32 option_value;
        socklen_t option_len;
+       rdpSettings* settings = tcp->settings;
 +      BOOL connected_to_proxy = FALSE;
++      const char *proxyTargetHostname = hostname;
++      int proxyTargetPort = port;
  
        if (!hostname)
                return FALSE;
        }
        else
        {
++              if (settings->HTTPProxyEnabled) {
++                      hostname = settings->HTTPProxyHostname;
++                      port = settings->HTTPProxyPort;
++                      connected_to_proxy = TRUE;
++              }
+ #ifdef NO_IPV6
                tcp->socketBio = BIO_new(BIO_s_connect());
                if (!tcp->socketBio)
                        return FALSE;
  
  
        tcp->bufferedBio = BIO_push(tcp->bufferedBio, tcp->socketBio);
  
-               if (!http_proxy_connect(tcp->bufferedBio, hostname, port))
 +      if (connected_to_proxy) {
++              if (!http_proxy_connect(tcp->bufferedBio, proxyTargetHostname, proxyTargetPort))
 +                      return FALSE;
 +      }
 +
        return TRUE;
  }
  
  #include "fastpath.h"
  #include "transport.h"
  #include "rdp.h"
 +#include "proxy.h"
  
+ #define TAG FREERDP_TAG("core.transport")
  #define BUFFER_SIZE 16384
  
  static void* transport_client_thread(void* arg);
@@@ -447,30 -474,20 +475,27 @@@ BOOL transport_connect(rdpTransport* tr
  {
        BOOL status = FALSE;
        rdpSettings* settings = transport->settings;
        transport->async = settings->AsyncTransport;
  
-       /* For TSGateway, find the system HTTPS proxy automatically */
-       if (transport->GatewayEnabled) {
+       if (transport->GatewayEnabled)
+       {
++              /* For TSGateway, find the system HTTPS proxy automatically */
 +              if (!transport->settings->HTTPProxyEnabled)
 +                      http_proxy_read_environment(settings, "https_proxy");
 +
 +              if (!transport->settings->HTTPProxyEnabled)
 +                      http_proxy_read_environment(settings, "HTTPS_PROXY");
-       }
 +
-       if (transport->GatewayEnabled)
-       {
                transport->layer = TRANSPORT_LAYER_TSG;
                transport->SplitInputOutput = TRUE;
-               transport->TcpOut = tcp_new(settings);
+               transport->TcpOut = freerdp_tcp_new(settings);
  
-               if (!tcp_connect(transport->TcpIn, settings->GatewayHostname, settings->GatewayPort) ||
-                               !tcp_set_blocking_mode(transport->TcpIn, FALSE))
+               if (!freerdp_tcp_connect(transport->TcpIn, settings->GatewayHostname, settings->GatewayPort, timeout) ||
+                               !freerdp_tcp_set_blocking_mode(transport->TcpIn, FALSE))
                        return FALSE;
  
-               if (!tcp_connect(transport->TcpOut, settings->GatewayHostname, settings->GatewayPort) ||
-                               !tcp_set_blocking_mode(transport->TcpOut, FALSE))
+               if (!freerdp_tcp_connect(transport->TcpOut, settings->GatewayHostname, settings->GatewayPort, timeout) ||
+                               !freerdp_tcp_set_blocking_mode(transport->TcpOut, FALSE))
                        return FALSE;
  
                if (!transport_tsg_connect(transport, hostname, port))