--- /dev/null
- * 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;
+}
+
#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;
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;
}