| 1 /** |
|
| 2 * @file udp_proxy_s5.c |
|
| 3 * |
|
| 4 * purple |
|
| 5 * |
|
| 6 * Purple is the legal property of its developers, whose names are too numerous |
|
| 7 * to list here. Please refer to the COPYRIGHT file distributed with this |
|
| 8 * source distribution. |
|
| 9 * |
|
| 10 * This program is free software; you can redistribute it and/or modify |
|
| 11 * it under the terms of the GNU General Public License as published by |
|
| 12 * the Free Software Foundation; either version 2 of the License, or |
|
| 13 * (at your option) any later version. |
|
| 14 * |
|
| 15 * This program is distributed in the hope that it will be useful, |
|
| 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 18 * GNU General Public License for more details. |
|
| 19 * |
|
| 20 * You should have received a copy of the GNU General Public License |
|
| 21 * along with this program; if not, write to the Free Software |
|
| 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA |
|
| 23 */ |
|
| 24 |
|
| 25 #include "debug.h" |
|
| 26 |
|
| 27 #include "udp_proxy_s5.h" |
|
| 28 |
|
| 29 static void _qq_s5_canread_again(gpointer data, gint source, PurpleInputCondition cond) |
|
| 30 { |
|
| 31 unsigned char buf[512]; |
|
| 32 struct PHB *phb = data; |
|
| 33 struct sockaddr_in sin; |
|
| 34 int len, error; |
|
| 35 socklen_t errlen; |
|
| 36 int flags; |
|
| 37 |
|
| 38 purple_input_remove(phb->inpa); |
|
| 39 purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Able to read again.\n"); |
|
| 40 |
|
| 41 len = read(source, buf, 10); |
|
| 42 if (len < 10) { |
|
| 43 purple_debug(PURPLE_DEBUG_WARNING, "socks5 proxy", "or not...\n"); |
|
| 44 close(source); |
|
| 45 |
|
| 46 if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { |
|
| 47 |
|
| 48 phb->func(phb->data, source, NULL); |
|
| 49 } |
|
| 50 |
|
| 51 g_free(phb->host); |
|
| 52 g_free(phb); |
|
| 53 return; |
|
| 54 } |
|
| 55 if ((buf[0] != 0x05) || (buf[1] != 0x00)) { |
|
| 56 if ((buf[0] == 0x05) && (buf[1] < 0x09)) |
|
| 57 purple_debug(PURPLE_DEBUG_ERROR, "socks5 proxy", "socks5 error: %x\n", buf[1]); |
|
| 58 else |
|
| 59 purple_debug(PURPLE_DEBUG_ERROR, "socks5 proxy", "Bad data.\n"); |
|
| 60 close(source); |
|
| 61 |
|
| 62 if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { |
|
| 63 |
|
| 64 phb->func(phb->data, -1, _("Unable to connect")); |
|
| 65 } |
|
| 66 |
|
| 67 g_free(phb->host); |
|
| 68 g_free(phb); |
|
| 69 return; |
|
| 70 } |
|
| 71 |
|
| 72 sin.sin_family = AF_INET; |
|
| 73 memcpy(&sin.sin_addr.s_addr, buf + 4, 4); |
|
| 74 memcpy(&sin.sin_port, buf + 8, 2); |
|
| 75 |
|
| 76 if (connect(phb->udpsock, (struct sockaddr *) &sin, sizeof(struct sockaddr_in)) < 0) { |
|
| 77 purple_debug(PURPLE_DEBUG_INFO, "s5_canread_again", "connect failed: %s\n", g_strerror(errno)); |
|
| 78 close(phb->udpsock); |
|
| 79 close(source); |
|
| 80 g_free(phb->host); |
|
| 81 g_free(phb); |
|
| 82 return; |
|
| 83 } |
|
| 84 |
|
| 85 error = ETIMEDOUT; |
|
| 86 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Connect didn't block\n"); |
|
| 87 errlen = sizeof(error); |
|
| 88 if (getsockopt(phb->udpsock, SOL_SOCKET, SO_ERROR, &error, &errlen) < 0) { |
|
| 89 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "getsockopt failed.\n"); |
|
| 90 close(phb->udpsock); |
|
| 91 return; |
|
| 92 } |
|
| 93 flags = fcntl(phb->udpsock, F_GETFL); |
|
| 94 fcntl(phb->udpsock, F_SETFL, flags & ~O_NONBLOCK); |
|
| 95 |
|
| 96 if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { |
|
| 97 phb->func(phb->data, phb->udpsock, NULL); |
|
| 98 } |
|
| 99 |
|
| 100 g_free(phb->host); |
|
| 101 g_free(phb); |
|
| 102 } |
|
| 103 |
|
| 104 static void _qq_s5_sendconnect(gpointer data, gint source) |
|
| 105 { |
|
| 106 unsigned char buf[512]; |
|
| 107 struct PHB *phb = data; |
|
| 108 struct sockaddr_in sin, ctlsin; |
|
| 109 int port; |
|
| 110 socklen_t ctllen; |
|
| 111 int flags; |
|
| 112 |
|
| 113 purple_debug(PURPLE_DEBUG_INFO, "s5_sendconnect", "remote host is %s:%d\n", phb->host, phb->port); |
|
| 114 |
|
| 115 buf[0] = 0x05; |
|
| 116 buf[1] = 0x03; /* udp relay */ |
|
| 117 buf[2] = 0x00; /* reserved */ |
|
| 118 buf[3] = 0x01; /* address type -- ipv4 */ |
|
| 119 memset(buf + 4, 0, 0x04); |
|
| 120 |
|
| 121 ctllen = sizeof(ctlsin); |
|
| 122 if (getsockname(source, (struct sockaddr *) &ctlsin, &ctllen) < 0) { |
|
| 123 purple_debug(PURPLE_DEBUG_INFO, "QQ", "getsockname: %s\n", g_strerror(errno)); |
|
| 124 close(source); |
|
| 125 g_free(phb->host); |
|
| 126 g_free(phb); |
|
| 127 return; |
|
| 128 } |
|
| 129 |
|
| 130 phb->udpsock = socket(PF_INET, SOCK_DGRAM, 0); |
|
| 131 purple_debug(PURPLE_DEBUG_INFO, "s5_sendconnect", "UDP socket=%d\n", phb->udpsock); |
|
| 132 if (phb->udpsock < 0) { |
|
| 133 close(source); |
|
| 134 g_free(phb->host); |
|
| 135 g_free(phb); |
|
| 136 return; |
|
| 137 } |
|
| 138 |
|
| 139 flags = fcntl(phb->udpsock, F_GETFL); |
|
| 140 fcntl(phb->udpsock, F_SETFL, flags | O_NONBLOCK); |
|
| 141 |
|
| 142 port = g_ntohs(ctlsin.sin_port) + 1; |
|
| 143 while (1) { |
|
| 144 inet_aton("0.0.0.0", &(sin.sin_addr)); |
|
| 145 sin.sin_family = AF_INET; |
|
| 146 sin.sin_port = g_htons(port); |
|
| 147 if (bind(phb->udpsock, (struct sockaddr *) &sin, sizeof(sin)) < 0) { |
|
| 148 port++; |
|
| 149 if (port > 65500) { |
|
| 150 close(source); |
|
| 151 g_free(phb->host); |
|
| 152 g_free(phb); |
|
| 153 return; |
|
| 154 } |
|
| 155 } else |
|
| 156 break; |
|
| 157 } |
|
| 158 |
|
| 159 memset(buf + 4, 0, 0x04); |
|
| 160 memcpy(buf + 8, &(sin.sin_port), 0x02); |
|
| 161 |
|
| 162 if (write(source, buf, 10) < 10) { |
|
| 163 close(source); |
|
| 164 purple_debug(PURPLE_DEBUG_INFO, "s5_sendconnect", "packet too small\n"); |
|
| 165 |
|
| 166 if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { |
|
| 167 phb->func(phb->data, -1, _("Unable to connect")); |
|
| 168 } |
|
| 169 |
|
| 170 g_free(phb->host); |
|
| 171 g_free(phb); |
|
| 172 return; |
|
| 173 } |
|
| 174 |
|
| 175 phb->inpa = purple_input_add(source, PURPLE_INPUT_READ, _qq_s5_canread_again, phb); |
|
| 176 } |
|
| 177 |
|
| 178 static void _qq_s5_readauth(gpointer data, gint source, PurpleInputCondition cond) |
|
| 179 { |
|
| 180 unsigned char buf[512]; |
|
| 181 struct PHB *phb = data; |
|
| 182 |
|
| 183 purple_input_remove(phb->inpa); |
|
| 184 purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Got auth response.\n"); |
|
| 185 |
|
| 186 if (read(source, buf, 2) < 2) { |
|
| 187 close(source); |
|
| 188 |
|
| 189 if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { |
|
| 190 |
|
| 191 phb->func(phb->data, -1, _("Unable to connect")); |
|
| 192 } |
|
| 193 |
|
| 194 g_free(phb->host); |
|
| 195 g_free(phb); |
|
| 196 return; |
|
| 197 } |
|
| 198 |
|
| 199 if ((buf[0] != 0x01) || (buf[1] != 0x00)) { |
|
| 200 close(source); |
|
| 201 |
|
| 202 if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { |
|
| 203 |
|
| 204 phb->func(phb->data, -1, _("Unable to connect")); |
|
| 205 } |
|
| 206 |
|
| 207 g_free(phb->host); |
|
| 208 g_free(phb); |
|
| 209 return; |
|
| 210 } |
|
| 211 |
|
| 212 _qq_s5_sendconnect(phb, source); |
|
| 213 } |
|
| 214 |
|
| 215 static void _qq_s5_canread(gpointer data, gint source, PurpleInputCondition cond) |
|
| 216 { |
|
| 217 unsigned char buf[512]; |
|
| 218 struct PHB *phb; |
|
| 219 int ret; |
|
| 220 |
|
| 221 phb = data; |
|
| 222 |
|
| 223 purple_input_remove(phb->inpa); |
|
| 224 purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Able to read.\n"); |
|
| 225 |
|
| 226 ret = read(source, buf, 2); |
|
| 227 if (ret < 2) { |
|
| 228 purple_debug(PURPLE_DEBUG_INFO, "s5_canread", "packet smaller than 2 octet\n"); |
|
| 229 close(source); |
|
| 230 |
|
| 231 if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { |
|
| 232 |
|
| 233 phb->func(phb->data, -1, _("Unable to connect")); |
|
| 234 } |
|
| 235 |
|
| 236 g_free(phb->host); |
|
| 237 g_free(phb); |
|
| 238 return; |
|
| 239 } |
|
| 240 |
|
| 241 if ((buf[0] != 0x05) || (buf[1] == 0xff)) { |
|
| 242 purple_debug(PURPLE_DEBUG_INFO, "s5_canread", "unsupport\n"); |
|
| 243 close(source); |
|
| 244 |
|
| 245 if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { |
|
| 246 |
|
| 247 phb->func(phb->data, -1, _("Unable to connect")); |
|
| 248 } |
|
| 249 |
|
| 250 g_free(phb->host); |
|
| 251 g_free(phb); |
|
| 252 return; |
|
| 253 } |
|
| 254 |
|
| 255 if (buf[1] == 0x02) { |
|
| 256 unsigned int i, j; |
|
| 257 |
|
| 258 i = strlen(purple_proxy_info_get_username(phb->gpi)); |
|
| 259 j = strlen(purple_proxy_info_get_password(phb->gpi)); |
|
| 260 |
|
| 261 buf[0] = 0x01; /* version 1 */ |
|
| 262 buf[1] = i; |
|
| 263 memcpy(buf + 2, purple_proxy_info_get_username(phb->gpi), i); |
|
| 264 buf[2 + i] = j; |
|
| 265 memcpy(buf + 2 + i + 1, purple_proxy_info_get_password(phb->gpi), j); |
|
| 266 |
|
| 267 if (write(source, buf, 3 + i + j) < 3 + i + j) { |
|
| 268 close(source); |
|
| 269 |
|
| 270 if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { |
|
| 271 |
|
| 272 phb->func(phb->data, -1, _("Unable to connect")); |
|
| 273 } |
|
| 274 |
|
| 275 g_free(phb->host); |
|
| 276 g_free(phb); |
|
| 277 return; |
|
| 278 } |
|
| 279 |
|
| 280 phb->inpa = purple_input_add(source, PURPLE_INPUT_READ, _qq_s5_readauth, phb); |
|
| 281 } else { |
|
| 282 purple_debug(PURPLE_DEBUG_INFO, "s5_canread", "calling s5_sendconnect\n"); |
|
| 283 _qq_s5_sendconnect(phb, source); |
|
| 284 } |
|
| 285 } |
|
| 286 |
|
| 287 static void _qq_s5_canwrite(gpointer data, gint source, PurpleInputCondition cond) |
|
| 288 { |
|
| 289 unsigned char buf[512]; |
|
| 290 int i; |
|
| 291 struct PHB *phb = data; |
|
| 292 socklen_t len; |
|
| 293 int error = ETIMEDOUT; |
|
| 294 int flags; |
|
| 295 |
|
| 296 purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Connected.\n"); |
|
| 297 |
|
| 298 if (phb->inpa > 0) |
|
| 299 purple_input_remove(phb->inpa); |
|
| 300 |
|
| 301 len = sizeof(error); |
|
| 302 if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { |
|
| 303 purple_debug(PURPLE_DEBUG_INFO, "getsockopt", "%s\n", g_strerror(errno)); |
|
| 304 close(source); |
|
| 305 if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { |
|
| 306 |
|
| 307 phb->func(phb->data, -1, _("Unable to connect")); |
|
| 308 } |
|
| 309 |
|
| 310 g_free(phb->host); |
|
| 311 g_free(phb); |
|
| 312 return; |
|
| 313 } |
|
| 314 flags = fcntl(source, F_GETFL); |
|
| 315 fcntl(source, F_SETFL, flags & ~O_NONBLOCK); |
|
| 316 |
|
| 317 i = 0; |
|
| 318 buf[0] = 0x05; /* SOCKS version 5 */ |
|
| 319 |
|
| 320 if (purple_proxy_info_get_username(phb->gpi) != NULL) { |
|
| 321 buf[1] = 0x02; /* two methods */ |
|
| 322 buf[2] = 0x00; /* no authentication */ |
|
| 323 buf[3] = 0x02; /* username/password authentication */ |
|
| 324 i = 4; |
|
| 325 } else { |
|
| 326 buf[1] = 0x01; |
|
| 327 buf[2] = 0x00; |
|
| 328 i = 3; |
|
| 329 } |
|
| 330 |
|
| 331 if (write(source, buf, i) < i) { |
|
| 332 purple_debug(PURPLE_DEBUG_INFO, "write", "%s\n", g_strerror(errno)); |
|
| 333 purple_debug(PURPLE_DEBUG_ERROR, "socks5 proxy", "Unable to write\n"); |
|
| 334 close(source); |
|
| 335 |
|
| 336 if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { |
|
| 337 |
|
| 338 phb->func(phb->data, -1, _("Unable to connect")); |
|
| 339 } |
|
| 340 |
|
| 341 g_free(phb->host); |
|
| 342 g_free(phb); |
|
| 343 return; |
|
| 344 } |
|
| 345 |
|
| 346 phb->inpa = purple_input_add(source, PURPLE_INPUT_READ, _qq_s5_canread, phb); |
|
| 347 } |
|
| 348 |
|
| 349 gint qq_proxy_socks5(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen) |
|
| 350 { |
|
| 351 gint fd; |
|
| 352 int flags; |
|
| 353 |
|
| 354 purple_debug(PURPLE_DEBUG_INFO, "QQ", |
|
| 355 "Connecting to %s:%d via %s:%d using SOCKS5\n", |
|
| 356 phb->host, phb->port, purple_proxy_info_get_host(phb->gpi), purple_proxy_info_get_port(phb->gpi)); |
|
| 357 |
|
| 358 if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0) |
|
| 359 return -1; |
|
| 360 |
|
| 361 purple_debug(PURPLE_DEBUG_INFO, "QQ", "proxy_sock5 return fd=%d\n", fd); |
|
| 362 |
|
| 363 flags = fcntl(fd, F_GETFL); |
|
| 364 fcntl(fd, F_SETFL, flags | O_NONBLOCK); |
|
| 365 if (connect(fd, addr, addrlen) < 0) { |
|
| 366 if ((errno == EINPROGRESS) || (errno == EINTR)) { |
|
| 367 purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Connect in asynchronous mode.\n"); |
|
| 368 phb->inpa = purple_input_add(fd, PURPLE_INPUT_WRITE, _qq_s5_canwrite, phb); |
|
| 369 } else { |
|
| 370 close(fd); |
|
| 371 return -1; |
|
| 372 } |
|
| 373 } else { |
|
| 374 purple_debug(PURPLE_DEBUG_MISC, "QQ", "Connect in blocking mode.\n"); |
|
| 375 flags = fcntl(fd, F_GETFL); |
|
| 376 fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); |
|
| 377 _qq_s5_canwrite(phb, fd, PURPLE_INPUT_WRITE); |
|
| 378 } |
|
| 379 |
|
| 380 return fd; |
|
| 381 } |
|