1 /* $OpenBSD: socks.c,v 1.15 2005/05/24 20:13:28 avsm Exp $ */
4 * Copyright (c) 1999 Niklas Hallqvist. All rights reserved.
5 * Copyright (c) 2004, 2005 Damien Miller. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
42 #define SOCKS_PORT "1080"
43 #define HTTP_PROXY_PORT "3128"
44 #define HTTP_MAXHDRS 64
47 #define SOCKS_NOAUTH 0
48 #define SOCKS_NOMETHOD 0xff
49 #define SOCKS_CONNECT 1
51 #define SOCKS_DOMAIN 3
54 int remote_connect(const char *, const char *, struct addrinfo
);
55 int socks_connect(const char *host
, const char *port
, struct addrinfo hints
,
56 const char *proxyhost
, const char *proxyport
, struct addrinfo proxyhints
,
60 decode_addrport(const char *h
, const char *p
, struct sockaddr
*addr
,
61 socklen_t addrlen
, int v4only
, int numeric
)
64 struct addrinfo hints
, *res
;
66 bzero(&hints
, sizeof(hints
));
67 hints
.ai_family
= v4only
? PF_INET
: PF_UNSPEC
;
68 hints
.ai_flags
= numeric
? AI_NUMERICHOST
: 0;
69 hints
.ai_socktype
= SOCK_STREAM
;
70 r
= getaddrinfo(h
, p
, &hints
, &res
);
71 /* Don't fatal when attempting to convert a numeric address */
74 errx(1, "getaddrinfo(\"%.64s\", \"%.64s\"): %s", h
, p
,
79 if (addrlen
< res
->ai_addrlen
) {
81 errx(1, "internal error: addrlen < res->ai_addrlen");
83 memcpy(addr
, res
->ai_addr
, res
->ai_addrlen
);
89 proxy_read_line(int fd
, char *buf
, size_t bufsz
)
95 errx(1, "proxy read too long");
96 if (atomicio(read
, fd
, buf
+ off
, 1) != 1)
101 if (buf
[off
] == '\n') {
111 socks_connect(const char *host
, const char *port
,
112 struct addrinfo hints
__attribute__ ((__unused__
)),
113 const char *proxyhost
, const char *proxyport
, struct addrinfo proxyhints
,
118 unsigned char buf
[1024];
120 struct sockaddr_storage addr
;
121 struct sockaddr_in
*in4
= (struct sockaddr_in
*)&addr
;
122 struct sockaddr_in6
*in6
= (struct sockaddr_in6
*)&addr
;
123 in_port_t serverport
;
125 if (proxyport
== NULL
)
126 proxyport
= (socksv
== -1) ? HTTP_PROXY_PORT
: SOCKS_PORT
;
128 proxyfd
= remote_connect(proxyhost
, proxyport
, proxyhints
);
133 /* Abuse API to lookup port */
134 if (decode_addrport("0.0.0.0", port
, (struct sockaddr
*)&addr
,
135 sizeof(addr
), 1, 1) == -1)
136 errx(1, "unknown port \"%.64s\"", port
);
137 serverport
= in4
->sin_port
;
140 if (decode_addrport(host
, port
, (struct sockaddr
*)&addr
,
141 sizeof(addr
), 0, 1) == -1)
142 addr
.ss_family
= 0; /* used in switch below */
144 /* Version 5, one method: no authentication */
147 buf
[2] = SOCKS_NOAUTH
;
148 cnt
= atomicio(vwrite
, proxyfd
, buf
, 3);
150 err(1, "write failed (%d/3)", cnt
);
152 cnt
= atomicio(read
, proxyfd
, buf
, 2);
154 err(1, "read failed (%d/3)", cnt
);
156 if (buf
[1] == SOCKS_NOMETHOD
)
157 errx(1, "authentication method negotiation failed");
159 switch (addr
.ss_family
) {
161 /* Version 5, connect: domain name */
163 /* Max domain name length is 255 bytes */
166 errx(1, "host name too long for SOCKS5");
168 buf
[1] = SOCKS_CONNECT
;
170 buf
[3] = SOCKS_DOMAIN
;
172 memcpy(buf
+ 5, host
, hlen
);
173 memcpy(buf
+ 5 + hlen
, &serverport
, sizeof serverport
);
177 /* Version 5, connect: IPv4 address */
179 buf
[1] = SOCKS_CONNECT
;
182 memcpy(buf
+ 4, &in4
->sin_addr
, sizeof in4
->sin_addr
);
183 memcpy(buf
+ 8, &in4
->sin_port
, sizeof in4
->sin_port
);
187 /* Version 5, connect: IPv6 address */
189 buf
[1] = SOCKS_CONNECT
;
192 memcpy(buf
+ 4, &in6
->sin6_addr
, sizeof in6
->sin6_addr
);
193 memcpy(buf
+ 20, &in6
->sin6_port
,
194 sizeof in6
->sin6_port
);
198 errx(1, "internal error: silly AF");
201 cnt
= atomicio(vwrite
, proxyfd
, buf
, wlen
);
203 err(1, "write failed (%d/%d)", cnt
, wlen
);
205 cnt
= atomicio(read
, proxyfd
, buf
, 10);
207 err(1, "read failed (%d/10)", cnt
);
209 errx(1, "connection failed, SOCKS error %d", buf
[1]);
210 } else if (socksv
== 4) {
211 /* This will exit on lookup failure */
212 decode_addrport(host
, port
, (struct sockaddr
*)&addr
,
217 buf
[1] = SOCKS_CONNECT
; /* connect */
218 memcpy(buf
+ 2, &in4
->sin_port
, sizeof in4
->sin_port
);
219 memcpy(buf
+ 4, &in4
->sin_addr
, sizeof in4
->sin_addr
);
220 buf
[8] = 0; /* empty username */
223 cnt
= atomicio(vwrite
, proxyfd
, buf
, wlen
);
225 err(1, "write failed (%d/%d)", cnt
, wlen
);
227 cnt
= atomicio(read
, proxyfd
, buf
, 8);
229 err(1, "read failed (%d/8)", cnt
);
231 errx(1, "connection failed, SOCKS error %d", buf
[1]);
232 } else if (socksv
== -1) {
233 /* HTTP proxy CONNECT */
235 /* Disallow bad chars in hostname */
236 if (strcspn(host
, "\r\n\t []:") != strlen(host
))
237 errx(1, "Invalid hostname");
239 /* Try to be sane about numeric IPv6 addresses */
240 if (strchr(host
, ':') != NULL
) {
241 r
= snprintf(buf
, sizeof(buf
),
242 "CONNECT [%s]:%d HTTP/1.0\r\n\r\n",
243 host
, ntohs(serverport
));
245 r
= snprintf(buf
, sizeof(buf
),
246 "CONNECT %s:%d HTTP/1.0\r\n\r\n",
247 host
, ntohs(serverport
));
249 if (r
== -1 || (size_t)r
>= sizeof(buf
))
250 errx(1, "hostname too long");
253 cnt
= atomicio(vwrite
, proxyfd
, buf
, r
);
255 err(1, "write failed (%d/%d)", cnt
, r
);
258 for (r
= 0; r
< HTTP_MAXHDRS
; r
++) {
259 proxy_read_line(proxyfd
, buf
, sizeof(buf
));
260 if (r
== 0 && strncmp(buf
, "HTTP/1.0 200 ", 12) != 0)
261 errx(1, "Proxy error: \"%s\"", buf
);
262 /* Discard headers until we hit an empty line */
267 errx(1, "Unknown proxy protocol %d", socksv
);