00001
00024
00025
00026 #include "debug.h"
00027
00028 #include "udp_proxy_s5.h"
00029
00030 extern gint
00031 _qq_fill_host(struct sockaddr_in *addr, const gchar * host, guint16 port);
00032
00033
00034 static void _qq_s5_canread_again(gpointer data, gint source, GaimInputCondition cond)
00035 {
00036 unsigned char buf[512];
00037 struct PHB *phb = data;
00038 struct sockaddr_in sin;
00039 int len;
00040
00041 gaim_input_remove(phb->inpa);
00042 gaim_debug(GAIM_DEBUG_INFO, "socks5 proxy", "Able to read again.\n");
00043
00044 len = read(source, buf, 10);
00045 if (len < 10) {
00046 gaim_debug(GAIM_DEBUG_WARNING, "socks5 proxy", "or not...\n");
00047 close(source);
00048
00049 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
00050
00051 phb->func(phb->data, source, GAIM_INPUT_READ);
00052 }
00053
00054 g_free(phb->host);
00055 g_free(phb);
00056 return;
00057 }
00058 if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
00059 if ((buf[0] == 0x05) && (buf[1] < 0x09))
00060 gaim_debug(GAIM_DEBUG_ERROR, "socks5 proxy", "socks5 error: %x\n", buf[1]);
00061 else
00062 gaim_debug(GAIM_DEBUG_ERROR, "socks5 proxy", "Bad data.\n");
00063 close(source);
00064
00065 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
00066
00067 phb->func(phb->data, -1, GAIM_INPUT_READ);
00068 }
00069
00070 g_free(phb->host);
00071 g_free(phb);
00072 return;
00073 }
00074
00075 sin.sin_family = AF_INET;
00076 memcpy(&sin.sin_addr.s_addr, buf + 4, 4);
00077 memcpy(&sin.sin_port, buf + 8, 2);
00078
00079 if (connect(phb->udpsock, (struct sockaddr *) &sin, sizeof(struct sockaddr_in)) < 0) {
00080 gaim_debug(GAIM_DEBUG_INFO, "s5_canread_again", "connect failed: %s\n", strerror(errno));
00081 close(phb->udpsock);
00082 close(source);
00083 g_free(phb->host);
00084 g_free(phb);
00085 return;
00086 }
00087
00088 int error = ETIMEDOUT;
00089 gaim_debug(GAIM_DEBUG_INFO, "QQ", "Connect didn't block\n");
00090 len = sizeof(error);
00091 if (getsockopt(phb->udpsock, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
00092 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "getsockopt failed.\n");
00093 close(phb->udpsock);
00094 return;
00095 }
00096 fcntl(phb->udpsock, F_SETFL, 0);
00097
00098 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
00099 phb->func(phb->data, phb->udpsock, GAIM_INPUT_READ);
00100 }
00101
00102 g_free(phb->host);
00103 g_free(phb);
00104 }
00105
00106
00107 static void _qq_s5_sendconnect(gpointer data, gint source)
00108 {
00109 unsigned char buf[512];
00110 struct PHB *phb = data;
00111 struct sockaddr_in sin, ctlsin;
00112 int port, ctllen;
00113
00114 gaim_debug(GAIM_DEBUG_INFO, "s5_sendconnect", "remote host is %s:%d\n", phb->host, phb->port);
00115
00116 buf[0] = 0x05;
00117 buf[1] = 0x03;
00118 buf[2] = 0x00;
00119 buf[3] = 0x01;
00120 memset(buf + 4, 0, 0x04);
00121
00122 ctllen = sizeof(ctlsin);
00123 if (getsockname(source, (struct sockaddr *) &ctlsin, &ctllen) < 0) {
00124 gaim_debug(GAIM_DEBUG_INFO, "QQ", "getsockname: %s\n", strerror(errno));
00125 close(source);
00126 g_free(phb->host);
00127 g_free(phb);
00128 return;
00129 }
00130
00131 phb->udpsock = socket(PF_INET, SOCK_DGRAM, 0);
00132 gaim_debug(GAIM_DEBUG_INFO, "s5_sendconnect", "UDP socket=%d\n", phb->udpsock);
00133 if (phb->udpsock < 0) {
00134 close(source);
00135 g_free(phb->host);
00136 g_free(phb);
00137 return;
00138 }
00139
00140 fcntl(phb->udpsock, F_SETFL, O_NONBLOCK);
00141
00142 port = ntohs(ctlsin.sin_port) + 1;
00143 while (1) {
00144 _qq_fill_host(&sin, "0.0.0.0", port);
00145 if (bind(phb->udpsock, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
00146 port++;
00147 if (port > 65500) {
00148 close(source);
00149 g_free(phb->host);
00150 g_free(phb);
00151 return;
00152 }
00153 } else
00154 break;
00155 }
00156
00157 memset(buf + 4, 0, 0x04);
00158 memcpy(buf + 8, &(sin.sin_port), 0x02);
00159
00160 if (write(source, buf, 10) < 10) {
00161 close(source);
00162 gaim_debug(GAIM_DEBUG_INFO, "s5_sendconnect", "packet too small\n");
00163
00164 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
00165 phb->func(phb->data, -1, GAIM_INPUT_READ);
00166 }
00167
00168 g_free(phb->host);
00169 g_free(phb);
00170 return;
00171 }
00172
00173 phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, _qq_s5_canread_again, phb);
00174 }
00175
00176
00177 static void _qq_s5_readauth(gpointer data, gint source, GaimInputCondition cond)
00178 {
00179 unsigned char buf[512];
00180 struct PHB *phb = data;
00181
00182 gaim_input_remove(phb->inpa);
00183 gaim_debug(GAIM_DEBUG_INFO, "socks5 proxy", "Got auth response.\n");
00184
00185 if (read(source, buf, 2) < 2) {
00186 close(source);
00187
00188 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
00189
00190 phb->func(phb->data, -1, GAIM_INPUT_READ);
00191 }
00192
00193 g_free(phb->host);
00194 g_free(phb);
00195 return;
00196 }
00197
00198 if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
00199 close(source);
00200
00201 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
00202
00203 phb->func(phb->data, -1, GAIM_INPUT_READ);
00204 }
00205
00206 g_free(phb->host);
00207 g_free(phb);
00208 return;
00209 }
00210
00211 _qq_s5_sendconnect(phb, source);
00212 }
00213
00214
00215 static void _qq_s5_canread(gpointer data, gint source, GaimInputCondition cond)
00216 {
00217 unsigned char buf[512];
00218 struct PHB *phb = data;
00219
00220 gaim_input_remove(phb->inpa);
00221 gaim_debug(GAIM_DEBUG_INFO, "socks5 proxy", "Able to read.\n");
00222
00223 int ret = read(source, buf, 2);
00224 if (ret < 2) {
00225 gaim_debug(GAIM_DEBUG_INFO, "s5_canread", "packet smaller than 2 octet\n");
00226 close(source);
00227
00228 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
00229
00230 phb->func(phb->data, source, GAIM_INPUT_READ);
00231 }
00232
00233 g_free(phb->host);
00234 g_free(phb);
00235 return;
00236 }
00237
00238 if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
00239 gaim_debug(GAIM_DEBUG_INFO, "s5_canread", "unsupport\n");
00240 close(source);
00241
00242 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
00243
00244 phb->func(phb->data, -1, GAIM_INPUT_READ);
00245 }
00246
00247 g_free(phb->host);
00248 g_free(phb);
00249 return;
00250 }
00251
00252 if (buf[1] == 0x02) {
00253 unsigned int i, j;
00254
00255 i = strlen(gaim_proxy_info_get_username(phb->gpi));
00256 j = strlen(gaim_proxy_info_get_password(phb->gpi));
00257
00258 buf[0] = 0x01;
00259 buf[1] = i;
00260 memcpy(buf + 2, gaim_proxy_info_get_username(phb->gpi), i);
00261 buf[2 + i] = j;
00262 memcpy(buf + 2 + i + 1, gaim_proxy_info_get_password(phb->gpi), j);
00263
00264 if (write(source, buf, 3 + i + j) < 3 + i + j) {
00265 close(source);
00266
00267 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
00268
00269 phb->func(phb->data, -1, GAIM_INPUT_READ);
00270 }
00271
00272 g_free(phb->host);
00273 g_free(phb);
00274 return;
00275 }
00276
00277 phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, _qq_s5_readauth, phb);
00278 } else {
00279 gaim_debug(GAIM_DEBUG_INFO, "s5_canread", "calling s5_sendconnect\n");
00280 _qq_s5_sendconnect(phb, source);
00281 }
00282 }
00283
00284
00285 void _qq_s5_canwrite(gpointer data, gint source, GaimInputCondition cond)
00286 {
00287 unsigned char buf[512];
00288 int i;
00289 struct PHB *phb = data;
00290 unsigned int len;
00291 int error = ETIMEDOUT;
00292
00293 gaim_debug(GAIM_DEBUG_INFO, "socks5 proxy", "Connected.\n");
00294
00295 if (phb->inpa > 0)
00296 gaim_input_remove(phb->inpa);
00297
00298 len = sizeof(error);
00299 if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
00300 gaim_debug(GAIM_DEBUG_INFO, "getsockopt", "%s\n", strerror(errno));
00301 close(source);
00302 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
00303
00304 phb->func(phb->data, -1, GAIM_INPUT_READ);
00305 }
00306
00307 g_free(phb->host);
00308 g_free(phb);
00309 return;
00310 }
00311 fcntl(source, F_SETFL, 0);
00312
00313 i = 0;
00314 buf[0] = 0x05;
00315
00316 if (gaim_proxy_info_get_username(phb->gpi) != NULL) {
00317 buf[1] = 0x02;
00318 buf[2] = 0x00;
00319 buf[3] = 0x02;
00320 i = 4;
00321 } else {
00322 buf[1] = 0x01;
00323 buf[2] = 0x00;
00324 i = 3;
00325 }
00326
00327 if (write(source, buf, i) < i) {
00328 gaim_debug(GAIM_DEBUG_INFO, "write", "%s\n", strerror(errno));
00329 gaim_debug(GAIM_DEBUG_ERROR, "socks5 proxy", "Unable to write\n");
00330 close(source);
00331
00332 if (phb->account == NULL || gaim_account_get_connection(phb->account) != NULL) {
00333
00334 phb->func(phb->data, -1, GAIM_INPUT_READ);
00335 }
00336
00337 g_free(phb->host);
00338 g_free(phb);
00339 return;
00340 }
00341
00342 phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, _qq_s5_canread, phb);
00343 }
00344
00345
00346 gint qq_proxy_socks5(struct PHB * phb, struct sockaddr * addr, socklen_t addrlen)
00347 {
00348
00349 gint fd;
00350 gaim_debug(GAIM_DEBUG_INFO, "QQ",
00351 "Connecting to %s:%d via %s:%d using SOCKS5\n",
00352 phb->host, phb->port, gaim_proxy_info_get_host(phb->gpi), gaim_proxy_info_get_port(phb->gpi));
00353
00354 if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0)
00355 return -1;
00356
00357 gaim_debug(GAIM_DEBUG_INFO, "QQ", "proxy_sock5 return fd=%d\n", fd);
00358
00359 fcntl(fd, F_SETFL, O_NONBLOCK);
00360 if (connect(fd, addr, addrlen) < 0) {
00361 if ((errno == EINPROGRESS) || (errno == EINTR)) {
00362 gaim_debug(GAIM_DEBUG_WARNING, "QQ", "Connect in asynchronous mode.\n");
00363 phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, _qq_s5_canwrite, phb);
00364 } else {
00365 close(fd);
00366 return -1;
00367 }
00368 } else {
00369 gaim_debug(GAIM_DEBUG_MISC, "QQ", "Connect in blocking mode.\n");
00370 fcntl(fd, F_SETFL, 0);
00371 _qq_s5_canwrite(phb, fd, GAIM_INPUT_WRITE);
00372 }
00373
00374 return fd;
00375 }