03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk/* $OpenBSD: socks.c,v 1.17 2006/09/25 04:51:20 ray Exp $ */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk/*
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * Copyright (c) 1999 Niklas Hallqvist. All rights reserved.
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * Copyright (c) 2004, 2005 Damien Miller. All rights reserved.
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk *
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * Redistribution and use in source and binary forms, with or without
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * modification, are permitted provided that the following conditions
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * are met:
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * 1. Redistributions of source code must retain the above copyright
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * notice, this list of conditions and the following disclaimer.
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * 2. Redistributions in binary form must reproduce the above copyright
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * notice, this list of conditions and the following disclaimer in the
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * documentation and/or other materials provided with the distribution.
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk *
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#pragma ident "%Z%%M% %I% %E% SMI"
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#include <sys/types.h>
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#include <sys/socket.h>
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#include <netinet/in.h>
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#include <arpa/inet.h>
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#include <err.h>
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#include <errno.h>
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#include <netdb.h>
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#include <stdio.h>
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#include <stdlib.h>
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#include <string.h>
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#include <unistd.h>
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#include <resolv.h>
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#include <strings.h>
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#include "atomicio.h"
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#define SOCKS_PORT "1080"
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#define HTTP_PROXY_PORT "3128"
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#define HTTP_MAXHDRS 64
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#define SOCKS_V5 5
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#define SOCKS_V4 4
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#define SOCKS_NOAUTH 0
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#define SOCKS_NOMETHOD 0xff
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#define SOCKS_CONNECT 1
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#define SOCKS_IPV4 1
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#define SOCKS_DOMAIN 3
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#define SOCKS_IPV6 4
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#define HTTP_10_407 "HTTP/1.0 407 "
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#define HTTP_10_200 "HTTP/1.0 200 "
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk#define HTTP_11_200 "HTTP/1.1 200 "
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vkint remote_connect(const char *, const char *, struct addrinfo);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vkint socks_connect(const char *, const char *,
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk const char *, const char *, struct addrinfo, int,
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk const char *);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk/*
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * Convert string representation of host (h) and service/port (p) into
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * sockaddr structure and return 0 on success, -1 on failure.
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * Indicate whether the host address is IPv4 (v4only) and numeric.
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vkstatic int
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vkdecode_addrport(const char *h, const char *p, struct sockaddr *addr,
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk socklen_t addrlen, int v4only, int numeric)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk{
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk int r;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk struct addrinfo hints, *res;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk bzero(&hints, sizeof (hints));
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk hints.ai_family = v4only ? PF_INET : PF_UNSPEC;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk hints.ai_flags = numeric ? AI_NUMERICHOST : 0;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk hints.ai_socktype = SOCK_STREAM;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk r = getaddrinfo(h, p, &hints, &res);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk /* Don't fatal when attempting to convert a numeric address */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (r != 0) {
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (!numeric) {
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk errx(1, "getaddrinfo(\"%.64s\", \"%.64s\"): %s", h, p,
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk gai_strerror(r));
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk }
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk return (-1);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk }
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (addrlen < res->ai_addrlen) {
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk freeaddrinfo(res);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk errx(1, "internal error: addrlen < res->ai_addrlen");
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk }
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk (void) memcpy(addr, res->ai_addr, res->ai_addrlen);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk freeaddrinfo(res);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk return (0);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk}
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk/*
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * Read single line from a descriptor into buffer up to bufsz bytes,
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * byte by byte. Returns length of the line (including ending NULL),
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * exits upon failure.
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vkstatic int
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vkproxy_read_line(int fd, char *buf, size_t bufsz)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk{
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk size_t off;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk for (off = 0; ; ) {
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (off >= bufsz)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk errx(1, "proxy read too long");
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (atomicio(read, fd, buf + off, 1) != 1)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk err(1, "proxy read");
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk /* Skip CR */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (buf[off] == '\r')
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk continue;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (buf[off] == '\n') {
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk buf[off] = '\0';
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk break;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk }
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk /*
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * we rewite \r\n to NULL since socks_connect() relies
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * on *buf being zero in that case.
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk off++;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk }
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk return (off);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk}
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk/*
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * Read proxy password from user and return it. The arguments are used
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * only for prompt construction.
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vkstatic const char *
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vkgetproxypass(const char *proxyuser, const char *proxyhost)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk{
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk char prompt[512];
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk const char *pw;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk (void) snprintf(prompt, sizeof (prompt), "Proxy password for %s@%s: ",
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk proxyuser, proxyhost);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if ((pw = getpassphrase(prompt)) == NULL)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk errx(1, "Unable to read proxy passphrase");
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk return (pw);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk}
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk/* perform connection via proxy using SOCKSv[45] or HTTP proxy CONNECT */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vkint
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vksocks_connect(const char *host, const char *port, const char *proxyhost,
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk const char *proxyport, struct addrinfo proxyhints, int socksv,
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk const char *proxyuser)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk{
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk int proxyfd, r, authretry = 0;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk size_t hlen, wlen;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk char buf[1024];
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk size_t cnt;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk struct sockaddr_storage addr;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk struct sockaddr_in *in4 = (struct sockaddr_in *)&addr;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&addr;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk in_port_t serverport;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk const char *proxypass = NULL;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (proxyport == NULL)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk proxyport = (socksv == -1) ? HTTP_PROXY_PORT : SOCKS_PORT;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk /* Abuse API to lookup port */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (decode_addrport("0.0.0.0", port, (struct sockaddr *)&addr,
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk sizeof (addr), 1, 1) == -1)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk errx(1, "unknown port \"%.64s\"", port);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk serverport = in4->sin_port;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vkagain:
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (authretry++ > 3)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk errx(1, "Too many authentication failures");
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk proxyfd = remote_connect(proxyhost, proxyport, proxyhints);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (proxyfd < 0)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk return (-1);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (socksv == 5) {
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (decode_addrport(host, port, (struct sockaddr *)&addr,
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk sizeof (addr), 0, 1) == -1)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk addr.ss_family = 0; /* used in switch below */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk /* Version 5, one method: no authentication */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk buf[0] = SOCKS_V5;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk buf[1] = 1;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk buf[2] = SOCKS_NOAUTH;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk cnt = atomicio(vwrite, proxyfd, buf, 3);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (cnt != 3)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk err(1, "write failed (%d/3)", cnt);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk cnt = atomicio(read, proxyfd, buf, 2);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (cnt != 2)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk err(1, "read failed (%d/3)", cnt);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if ((unsigned char)buf[1] == SOCKS_NOMETHOD)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk errx(1, "authentication method negotiation failed");
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk switch (addr.ss_family) {
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk case 0:
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk /* Version 5, connect: domain name */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk /* Max domain name length is 255 bytes */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk hlen = strlen(host);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (hlen > 255)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk errx(1, "host name too long for SOCKS5");
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk buf[0] = SOCKS_V5;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk buf[1] = SOCKS_CONNECT;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk buf[2] = 0;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk buf[3] = SOCKS_DOMAIN;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk buf[4] = hlen;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk (void) memcpy(buf + 5, host, hlen);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk (void) memcpy(buf + 5 + hlen, &serverport,
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk sizeof (serverport));
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk wlen = 5 + hlen + sizeof (serverport);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk break;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk case AF_INET:
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk /* Version 5, connect: IPv4 address */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk buf[0] = SOCKS_V5;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk buf[1] = SOCKS_CONNECT;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk buf[2] = 0;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk buf[3] = SOCKS_IPV4;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk (void) memcpy(buf + 4, &in4->sin_addr,
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk sizeof (in4->sin_addr));
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk (void) memcpy(buf + 8, &in4->sin_port,
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk sizeof (in4->sin_port));
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk wlen = 4 + sizeof (in4->sin_addr) +
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk sizeof (in4->sin_port);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk break;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk case AF_INET6:
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk /* Version 5, connect: IPv6 address */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk buf[0] = SOCKS_V5;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk buf[1] = SOCKS_CONNECT;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk buf[2] = 0;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk buf[3] = SOCKS_IPV6;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk (void) memcpy(buf + 4, &in6->sin6_addr,
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk sizeof (in6->sin6_addr));
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk (void) memcpy(buf + 20, &in6->sin6_port,
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk sizeof (in6->sin6_port));
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk wlen = 4 + sizeof (in6->sin6_addr) +
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk sizeof (in6->sin6_port);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk break;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk default:
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk errx(1, "internal error: silly AF");
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk }
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk cnt = atomicio(vwrite, proxyfd, buf, wlen);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (cnt != wlen)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk err(1, "write failed (%d/%d)", cnt, wlen);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk /*
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * read proxy reply which is 4 byte "header", BND.ADDR
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * and BND.PORT according to RFC 1928, section 6. BND.ADDR
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * is 4 bytes in case of IPv4 which gives us 10 bytes in sum.
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk cnt = atomicio(read, proxyfd, buf, 10);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (cnt != 10)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk err(1, "read failed (%d/10)", cnt);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (buf[1] != 0)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk errx(1, "connection failed, SOCKS error %d", buf[1]);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk } else if (socksv == 4) {
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk /* This will exit on lookup failure */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk (void) decode_addrport(host, port, (struct sockaddr *)&addr,
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk sizeof (addr), 1, 0);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk /* Version 4 */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk buf[0] = SOCKS_V4;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk buf[1] = SOCKS_CONNECT; /* connect */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk (void) memcpy(buf + 2, &in4->sin_port, sizeof (in4->sin_port));
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk (void) memcpy(buf + 4, &in4->sin_addr, sizeof (in4->sin_addr));
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk buf[8] = 0; /* empty username */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk wlen = 9;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk cnt = atomicio(vwrite, proxyfd, buf, wlen);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (cnt != wlen)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk err(1, "write failed (%d/%d)", cnt, wlen);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk /*
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * SOCKSv4 proxy replies consists of 2 byte "header",
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk * port number and numeric IPv4 address which gives 8 bytes.
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk cnt = atomicio(read, proxyfd, buf, 8);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (cnt != 8)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk err(1, "read failed (%d/8)", cnt);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (buf[1] != 90)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk errx(1, "connection failed, SOCKS error %d", buf[1]);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk } else if (socksv == -1) {
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk /* HTTP proxy CONNECT according to RFC 2817, section 5 */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk /* Disallow bad chars in hostname */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (strcspn(host, "\r\n\t []:") != strlen(host))
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk errx(1, "Invalid hostname");
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk /* Try to be sane about numeric IPv6 addresses */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (strchr(host, ':') != NULL) {
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk r = snprintf(buf, sizeof (buf),
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk "CONNECT [%s]:%d HTTP/1.0\r\n",
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk host, ntohs(serverport));
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk } else {
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk r = snprintf(buf, sizeof (buf),
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk "CONNECT %s:%d HTTP/1.0\r\n",
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk host, ntohs(serverport));
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk }
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (r == -1 || (size_t)r >= sizeof (buf))
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk errx(1, "hostname too long");
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk r = strlen(buf);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk cnt = atomicio(vwrite, proxyfd, buf, r);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (cnt != r)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk err(1, "write failed (%d/%d)", cnt, r);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (authretry > 1) {
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk char resp[1024];
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk proxypass = getproxypass(proxyuser, proxyhost);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk r = snprintf(buf, sizeof (buf), "%s:%s",
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk proxyuser, proxypass);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk free((void *)proxypass);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (r == -1 || (size_t)r >= sizeof (buf) ||
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk b64_ntop((unsigned char *)buf, strlen(buf), resp,
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk sizeof (resp)) == -1)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk errx(1, "Proxy username/password too long");
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk r = snprintf(buf, sizeof (buf), "Proxy-Authorization: "
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk "Basic %s\r\n", resp);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (r == -1 || (size_t)r >= sizeof (buf))
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk errx(1, "Proxy auth response too long");
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk r = strlen(buf);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if ((cnt = atomicio(vwrite, proxyfd, buf, r)) != r)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk err(1, "write failed (%d/%d)", cnt, r);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk }
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk /* Terminate headers */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if ((r = atomicio(vwrite, proxyfd, "\r\n", 2)) != 2)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk err(1, "write failed (2/%d)", r);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk /* Read status reply */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk (void) proxy_read_line(proxyfd, buf, sizeof (buf));
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (proxyuser != NULL &&
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk strncmp(buf, HTTP_10_407, strlen(HTTP_10_407)) == 0) {
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (authretry > 1) {
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk (void) fprintf(stderr, "Proxy authentication "
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk "failed\n");
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk }
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk (void) close(proxyfd);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk goto again;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk } else if (strncmp(buf, HTTP_10_200,
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk strlen(HTTP_10_200)) != 0 && strncmp(buf, HTTP_11_200,
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk strlen(HTTP_11_200)) != 0)
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk errx(1, "Proxy error: \"%s\"", buf);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk /* Headers continue until we hit an empty line */
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk for (r = 0; r < HTTP_MAXHDRS; r++) {
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk (void) proxy_read_line(proxyfd, buf, sizeof (buf));
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (*buf == '\0')
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk break;
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk }
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk if (*buf != '\0')
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk errx(1, "Too many proxy headers received");
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk } else
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk errx(1, "Unknown proxy protocol %d", socksv);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk return (proxyfd);
03100a6332bd4edc7a53091fcf7c9a7131bcdaa7vk}