diff options
author | djm <> | 2005-05-20 22:46:08 +0000 |
---|---|---|
committer | djm <> | 2005-05-20 22:46:08 +0000 |
commit | 6ce3f0a104c8122e9c64fc28f445a618787e673f (patch) | |
tree | 6c4166b889992c386692c8ad94c4f5d213c4ece5 | |
parent | ee77b0e2617f3d20ec936711a68014c5fc7d5929 (diff) | |
download | openbsd-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.c | 158 |
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 | 53 | ssize_t atomicio(ssize_t (*)(int, void *, size_t), int, void *, size_t); | |
51 | int remote_connect(const char *, const char *, struct addrinfo); | 54 | int remote_connect(const char *, const char *, struct addrinfo); |
52 | int socks_connect(const char *host, const char *port, struct addrinfo hints, | 55 | int 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 | ||
56 | static in_addr_t | 59 | static int |
57 | decode_addr(const char *s) | 60 | decode_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 | ||
69 | static in_port_t | 66 | bzero(&hints, sizeof(hints)); |
70 | decode_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 | ||
87 | static int | 88 | static 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) |