diff options
-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) |