summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordjm <>2005-05-20 22:46:08 +0000
committerdjm <>2005-05-20 22:46:08 +0000
commit6ce3f0a104c8122e9c64fc28f445a618787e673f (patch)
tree6c4166b889992c386692c8ad94c4f5d213c4ece5
parentee77b0e2617f3d20ec936711a68014c5fc7d5929 (diff)
downloadopenbsd-6ce3f0a104c8122e9c64fc28f445a618787e673f.tar.gz
openbsd-6ce3f0a104c8122e9c64fc28f445a618787e673f.tar.bz2
openbsd-6ce3f0a104c8122e9c64fc28f445a618787e673f.zip
Teach the SOCKS5 code more of the protocol, so it can send domain names
to the proxy instead of resolving them locally and sending IPv4 addresses. This improves privacy, e.g. when using nc with OpenSSH DynamicForward tunnels, and gives us better IPv6 support; ok beck@
-rw-r--r--src/usr.bin/nc/socks.c158
1 files changed, 102 insertions, 56 deletions
diff --git a/src/usr.bin/nc/socks.c b/src/usr.bin/nc/socks.c
index ca67c6df59..7380b7999d 100644
--- a/src/usr.bin/nc/socks.c
+++ b/src/usr.bin/nc/socks.c
@@ -1,7 +1,8 @@
1/* $OpenBSD: socks.c,v 1.13 2005/05/20 11:06:58 djm Exp $ */ 1/* $OpenBSD: socks.c,v 1.14 2005/05/20 22:46:08 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 1999 Niklas Hallqvist. All rights reserved. 4 * Copyright (c) 1999 Niklas Hallqvist. All rights reserved.
5 * Copyright (c) 2004, 2005 Damien Miller. All rights reserved.
5 * 6 *
6 * Redistribution and use in source and binary forms, with or without 7 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions 8 * modification, are permitted provided that the following conditions
@@ -46,42 +47,42 @@
46#define SOCKS_NOMETHOD 0xff 47#define SOCKS_NOMETHOD 0xff
47#define SOCKS_CONNECT 1 48#define SOCKS_CONNECT 1
48#define SOCKS_IPV4 1 49#define SOCKS_IPV4 1
50#define SOCKS_DOMAIN 3
51#define SOCKS_IPV6 4
49 52
50 53ssize_t atomicio(ssize_t (*)(int, void *, size_t), int, void *, size_t);
51int remote_connect(const char *, const char *, struct addrinfo); 54int remote_connect(const char *, const char *, struct addrinfo);
52int socks_connect(const char *host, const char *port, struct addrinfo hints, 55int socks_connect(const char *host, const char *port, struct addrinfo hints,
53 const char *proxyhost, const char *proxyport, struct addrinfo proxyhints, 56 const char *proxyhost, const char *proxyport, struct addrinfo proxyhints,
54 int socksv); 57 int socksv);
55 58
56static in_addr_t 59static int
57decode_addr(const char *s) 60decode_addrport(const char *h, const char *p, struct sockaddr *addr,
61 socklen_t addrlen, int v4only, int numeric)
58{ 62{
59 struct hostent *hp = gethostbyname(s); 63 int r;
60 struct in_addr retval; 64 struct addrinfo hints, *res;
61
62 if (hp)
63 return (*(in_addr_t *)hp->h_addr_list[0]);
64 if (inet_aton(s, &retval))
65 return (retval.s_addr);
66 errx(1, "cannot decode address \"%s\"", s);
67}
68 65
69static in_port_t 66 bzero(&hints, sizeof(hints));
70decode_port(const char *s) 67 hints.ai_family = v4only ? PF_INET : PF_UNSPEC;
71{ 68 hints.ai_flags = numeric ? AI_NUMERICHOST : 0;
72 struct servent *sp; 69 hints.ai_socktype = SOCK_STREAM;
73 in_port_t port; 70 r = getaddrinfo(h, p, &hints, &res);
74 char *p; 71 /* Don't fatal when attempting to convert a numeric address */
75 72 if (r != 0) {
76 port = strtol(s, &p, 10); 73 if (!numeric) {
77 if (s == p) { 74 errx(1, "getaddrinfo(\"%.64s\", \"%.64s\"): %s", h, p,
78 sp = getservbyname(s, "tcp"); 75 gai_strerror(r));
79 if (sp) 76 }
80 return (sp->s_port); 77 return (-1);
78 }
79 if (addrlen < res->ai_addrlen) {
80 freeaddrinfo(res);
81 errx(1, "internal error: addrlen < res->ai_addrlen");
81 } 82 }
82 if (*s != '\0' && *p == '\0') 83 memcpy(addr, res->ai_addr, res->ai_addrlen);
83 return (htons(port)); 84 freeaddrinfo(res);
84 errx (1, "cannot decode port \"%s\"", s); 85 return (0);
85} 86}
86 87
87static int 88static int
@@ -116,9 +117,12 @@ socks_connect(const char *host, const char *port,
116 int socksv) 117 int socksv)
117{ 118{
118 int proxyfd, r; 119 int proxyfd, r;
120 size_t hlen, wlen;
119 unsigned char buf[1024]; 121 unsigned char buf[1024];
120 ssize_t cnt; 122 ssize_t cnt;
121 in_addr_t serveraddr; 123 struct sockaddr_storage addr;
124 struct sockaddr_in *in4 = (struct sockaddr_in *)&addr;
125 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&addr;
122 in_port_t serverport; 126 in_port_t serverport;
123 127
124 if (proxyport == NULL) 128 if (proxyport == NULL)
@@ -129,12 +133,17 @@ socks_connect(const char *host, const char *port,
129 if (proxyfd < 0) 133 if (proxyfd < 0)
130 return (-1); 134 return (-1);
131 135
132 /* HTTP proxies should use hostnames. (XXX so can SOCKS5) */ 136 /* Abuse API to lookup port */
133 if (socksv != -1) 137 if (decode_addrport("0.0.0.0", port, (struct sockaddr *)&addr,
134 serveraddr = decode_addr(host); 138 sizeof(addr), 1, 1) == -1)
135 serverport = decode_port(port); 139 errx(1, "unknown port \"%.64s\"", port);
140 serverport = in4->sin_port;
136 141
137 if (socksv == 5) { 142 if (socksv == 5) {
143 if (decode_addrport(host, port, (struct sockaddr *)&addr,
144 sizeof(addr), 0, 1) == -1)
145 addr.ss_family = 0; /* used in switch below */
146
138 /* Version 5, one method: no authentication */ 147 /* Version 5, one method: no authentication */
139 buf[0] = SOCKS_V5; 148 buf[0] = SOCKS_V5;
140 buf[1] = 1; 149 buf[1] = 1;
@@ -149,23 +158,56 @@ socks_connect(const char *host, const char *port,
149 if (buf[1] == SOCKS_NOMETHOD) 158 if (buf[1] == SOCKS_NOMETHOD)
150 errx(1, "authentication method negotiation failed"); 159 errx(1, "authentication method negotiation failed");
151 160
152 /* Version 5, connect: IPv4 address */ 161 switch (addr.ss_family) {
153 buf[0] = SOCKS_V5; 162 case 0:
154 buf[1] = SOCKS_CONNECT; 163 /* Version 5, connect: domain name */
155 buf[2] = 0; 164
156 buf[3] = SOCKS_IPV4; 165 /* Max domain name length is 255 bytes */
157 memcpy(buf + 4, &serveraddr, sizeof serveraddr); 166 hlen = strlen(host);
158 memcpy(buf + 8, &serverport, sizeof serverport); 167 if (hlen > 255)
159 168 errx(1, "host name too long for SOCKS5");
160 /* XXX Handle short writes better */ 169 buf[0] = SOCKS_V5;
161 cnt = write(proxyfd, buf, 10); 170 buf[1] = SOCKS_CONNECT;
171 buf[2] = 0;
172 buf[3] = SOCKS_DOMAIN;
173 buf[4] = hlen;
174 memcpy(buf + 5, host, hlen);
175 memcpy(buf + 5 + hlen, &serverport, sizeof serverport);
176 wlen = 7 + hlen;
177 break;
178 case AF_INET:
179 /* Version 5, connect: IPv4 address */
180 buf[0] = SOCKS_V5;
181 buf[1] = SOCKS_CONNECT;
182 buf[2] = 0;
183 buf[3] = SOCKS_IPV4;
184 memcpy(buf + 4, &in4->sin_addr, sizeof in4->sin_addr);
185 memcpy(buf + 8, &in4->sin_port, sizeof in4->sin_port);
186 wlen = 10;
187 break;
188 case AF_INET6:
189 /* Version 5, connect: IPv6 address */
190 buf[0] = SOCKS_V5;
191 buf[1] = SOCKS_CONNECT;
192 buf[2] = 0;
193 buf[3] = SOCKS_IPV6;
194 memcpy(buf + 4, &in6->sin6_addr, sizeof in6->sin6_addr);
195 memcpy(buf + 20, &in6->sin6_port,
196 sizeof in6->sin6_port);
197 wlen = 22;
198 break;
199 default:
200 errx(1, "internal error: silly AF");
201 }
202
203 cnt = atomicio((ssize_t (*)(int, void *, size_t))write,
204 proxyfd, buf, wlen);
162 if (cnt == -1) 205 if (cnt == -1)
163 err(1, "write failed"); 206 err(1, "write failed");
164 if (cnt != 10) 207 if (cnt != wlen)
165 errx(1, "short write, %d (expected 10)", cnt); 208 errx(1, "short write, %d (expected %d)", cnt, wlen);
166 209
167 /* XXX Handle short reads better */ 210 cnt = atomicio(read, proxyfd, buf, 10);
168 cnt = read(proxyfd, buf, 10);
169 if (cnt == -1) 211 if (cnt == -1)
170 err(1, "read failed"); 212 err(1, "read failed");
171 if (cnt != 10) 213 if (cnt != 10)
@@ -173,21 +215,25 @@ socks_connect(const char *host, const char *port,
173 if (buf[1] != 0) 215 if (buf[1] != 0)
174 errx(1, "connection failed, SOCKS error %d", buf[1]); 216 errx(1, "connection failed, SOCKS error %d", buf[1]);
175 } else if (socksv == 4) { 217 } else if (socksv == 4) {
218 /* This will exit on lookup failure */
219 decode_addrport(host, port, (struct sockaddr *)&addr,
220 sizeof(addr), 1, 0);
221
176 /* Version 4 */ 222 /* Version 4 */
177 buf[0] = SOCKS_V4; 223 buf[0] = SOCKS_V4;
178 buf[1] = SOCKS_CONNECT; /* connect */ 224 buf[1] = SOCKS_CONNECT; /* connect */
179 memcpy(buf + 2, &serverport, sizeof serverport); 225 memcpy(buf + 2, &in4->sin_port, sizeof in4->sin_port);
180 memcpy(buf + 4, &serveraddr, sizeof serveraddr); 226 memcpy(buf + 4, &in4->sin_addr, sizeof in4->sin_addr);
181 buf[8] = 0; /* empty username */ 227 buf[8] = 0; /* empty username */
228 wlen = 9;
182 229
183 cnt = write(proxyfd, buf, 9); 230 cnt = write(proxyfd, buf, wlen);
184 if (cnt == -1) 231 if (cnt == -1)
185 err(1, "write failed"); 232 err(1, "write failed");
186 if (cnt != 9) 233 if (cnt != wlen)
187 errx(1, "short write, %d (expected 9)", cnt); 234 errx(1, "short write, %d (expected %d)", cnt, wlen);
188 235
189 /* XXX Handle short reads better */ 236 cnt = atomicio(read, proxyfd, buf, 8);
190 cnt = read(proxyfd, buf, 8);
191 if (cnt == -1) 237 if (cnt == -1)
192 err(1, "read failed"); 238 err(1, "read failed");
193 if (cnt != 8) 239 if (cnt != 8)
@@ -215,8 +261,8 @@ socks_connect(const char *host, const char *port,
215 errx(1, "hostname too long"); 261 errx(1, "hostname too long");
216 r = strlen(buf); 262 r = strlen(buf);
217 263
218 /* XXX atomicio */ 264 cnt = atomicio((ssize_t (*)(int, void *, size_t))write,
219 cnt = write(proxyfd, buf, r); 265 proxyfd, buf, r);
220 if (cnt == -1) 266 if (cnt == -1)
221 err(1, "write failed"); 267 err(1, "write failed");
222 if (cnt != r) 268 if (cnt != r)