Mercurial > pidgin
annotate src/protocols/icq/tcplink.c @ 3865:66b8dcba9751
[gaim-migrate @ 4017]
change a // to a /* ... */ i'm surprised someone hasn't complained before now
committer: Tailor Script <tailor@pidgin.im>
author | Luke Schierer <lschiere@pidgin.im> |
---|---|
date | Sun, 03 Nov 2002 01:19:45 +0000 |
parents | f0a2a9afdb77 |
children |
rev | line source |
---|---|
2086 | 1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | |
3 /* | |
2496
f0a2a9afdb77
[gaim-migrate @ 2509]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2086
diff
changeset
|
4 * $Id: tcplink.c 2509 2001-10-13 00:06:18Z warmenhoven $ |
2086 | 5 * |
6 * Copyright (C) 1998-2001, Denis V. Dmitrienko <denis@null.net> and | |
7 * Bill Soudan <soudan@kde.org> | |
8 * | |
9 * This program is free software; you can redistribute it and/or modify | |
10 * it under the terms of the GNU General Public License as published by | |
11 * the Free Software Foundation; either version 2 of the License, or | |
12 * (at your option) any later version. | |
13 * | |
14 * This program is distributed in the hope that it will be useful, | |
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 * GNU General Public License for more details. | |
18 * | |
19 * You should have received a copy of the GNU General Public License | |
20 * along with this program; if not, write to the Free Software | |
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
22 * | |
23 */ | |
24 | |
25 #include <stdlib.h> | |
26 | |
27 #include <fcntl.h> | |
28 #include <errno.h> | |
29 | |
30 #ifdef _WIN32 | |
31 #include <winsock.h> | |
32 #define EINPROGRESS WSAEINPROGRESS | |
33 #define ENETUNREACH WSAENETUNREACH | |
34 #define ECONNREFUSED WSAECONNREFUSED | |
35 #define ETIMEDOUT WSAETIMEDOUT | |
36 #define EOPNOTSUPP WSAEOPNOTSUPP | |
37 #define EAFNOSUPPORT WSAEAFNOSUPPORT | |
38 #define EWOULDBLOCK WSAEWOULDBLOCK | |
39 #else | |
40 #include <netdb.h> | |
41 #endif | |
42 | |
43 #include "icqlib.h" | |
44 #include "stdpackets.h" | |
45 #include "tcp.h" | |
46 #include "errno.h" | |
47 #include "chatsession.h" | |
48 #include "filesession.h" | |
49 #include "contacts.h" | |
2496
f0a2a9afdb77
[gaim-migrate @ 2509]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2086
diff
changeset
|
50 #include "socketmanager.h" |
2086 | 51 |
52 icq_TCPLink *icq_TCPLinkNew(icq_Link *icqlink) | |
53 { | |
54 icq_TCPLink *p=(icq_TCPLink *)malloc(sizeof(icq_TCPLink)); | |
55 | |
56 p->socket=-1; | |
57 p->icqlink=icqlink; | |
58 p->mode=0; | |
59 p->session=0L; | |
60 p->type=TCP_LINK_MESSAGE; | |
61 p->buffer_count=0; | |
62 p->send_queue=icq_ListNew(); | |
63 p->received_queue=icq_ListNew(); | |
64 p->id=0; | |
65 p->remote_uin=0; | |
66 p->remote_version=0; | |
67 p->flags=0; | |
68 p->proxy_status = 0; | |
69 p->connect_timeout = NULL; | |
70 | |
71 if(p) | |
72 icq_ListEnqueue(icqlink->d->icq_TCPLinks, p); | |
73 | |
74 return p; | |
75 } | |
76 | |
77 int _icq_TCPLinkDelete(void *pv, va_list data) | |
78 { | |
79 icq_Packet *p=(icq_Packet *)pv; | |
80 icq_Link *icqlink=va_arg(data, icq_Link *); | |
81 | |
82 /* notify the app the packet didn't make it */ | |
83 if(p->id) | |
84 invoke_callback(icqlink, icq_RequestNotify)(icqlink, p->id, | |
85 ICQ_NOTIFY_FAILED, 0, 0); | |
86 | |
87 return 0; | |
88 } | |
89 | |
90 void icq_TCPLinkDelete(void *pv) | |
91 { | |
92 icq_TCPLink *p=(icq_TCPLink *)pv; | |
93 | |
94 /* process anything left in the received queue */ | |
95 icq_TCPLinkProcessReceived(p); | |
96 | |
97 /* make sure we notify app that packets in send queue didn't make it */ | |
98 (void)icq_ListTraverse(p->send_queue, _icq_TCPLinkDelete, p->icqlink); | |
99 | |
100 /* destruct all packets still waiting on queues */ | |
101 icq_ListDelete(p->send_queue, icq_PacketDelete); | |
102 icq_ListDelete(p->received_queue, icq_PacketDelete); | |
103 | |
104 /* if this is a chat or file link, delete the associated session as | |
105 * well, but make sure we unassociate ourself first so the session | |
106 * doesn't try to close us */ | |
107 if(p->session) | |
108 { | |
109 if(p->type==TCP_LINK_CHAT) | |
110 { | |
111 icq_ChatSession *psession=p->session; | |
112 psession->tcplink=NULL; | |
113 icq_ChatSessionClose(psession); | |
114 } | |
115 | |
116 if(p->type==TCP_LINK_FILE) { | |
117 icq_FileSession *psession=p->session; | |
118 psession->tcplink=NULL; | |
119 icq_FileSessionClose(psession); | |
120 } | |
121 } | |
122 | |
123 /* close the socket after we notify app so app can read errno if necessary */ | |
124 if (p->socket > -1) | |
125 { | |
126 icq_SocketDelete(p->socket); | |
127 } | |
128 | |
129 if (p->connect_timeout) | |
130 { | |
131 icq_TimeoutDelete(p->connect_timeout); | |
132 } | |
133 | |
134 free(p); | |
135 } | |
136 | |
137 void icq_TCPLinkClose(icq_TCPLink *plink) | |
138 { | |
139 icq_ListRemove(plink->icqlink->d->icq_TCPLinks, plink); | |
140 icq_TCPLinkDelete(plink); | |
141 } | |
142 | |
143 int icq_TCPLinkProxyConnect(icq_TCPLink *plink, DWORD uin, int port) | |
144 { | |
145 struct sockaddr_in prsin; | |
146 struct hostent *host_struct; | |
147 int conct; | |
148 | |
149 (void)uin; (void)port; | |
150 | |
151 prsin.sin_addr.s_addr = htonl(plink->icqlink->icq_ProxyIP); | |
152 if(prsin.sin_addr.s_addr == (unsigned long)-1) | |
153 { | |
154 prsin.sin_addr.s_addr = inet_addr(plink->icqlink->icq_ProxyHost); | |
155 if(prsin.sin_addr.s_addr == (unsigned long)-1) /* name isn't n.n.n.n so must be DNS */ | |
156 { | |
157 host_struct = gethostbyname(plink->icqlink->icq_ProxyHost); | |
158 if(host_struct == 0L) | |
159 { | |
160 icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Can't find hostname: %s\n", | |
161 plink->icqlink->icq_ProxyHost); | |
162 return -1; | |
163 } | |
164 prsin.sin_addr = *((struct in_addr *)host_struct->h_addr); | |
165 } | |
166 } | |
167 prsin.sin_family = AF_INET; /* we're using the inet not appletalk*/ | |
168 prsin.sin_port = htons(plink->icqlink->icq_ProxyPort); /* port */ | |
169 /* flags = fcntl(plink->socket, F_GETFL, 0); */ | |
170 /* fcntl(plink->socket, F_SETFL, flags & (~O_NONBLOCK)); */ | |
171 plink->mode |= TCP_LINK_SOCKS_CONNECTING; | |
172 conct = connect(plink->socket, (struct sockaddr *) &prsin, sizeof(prsin)); | |
173 if(conct == -1) /* did we connect ?*/ | |
174 { | |
175 if(errno != EINPROGRESS) | |
176 { | |
177 conct = errno; | |
178 icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Connection refused\n"); | |
179 return conct; | |
180 } | |
181 return 1; | |
182 } | |
183 return 0; | |
184 } | |
185 | |
186 int icq_TCPLinkProxyRequestAuthorization(icq_TCPLink *plink) | |
187 { | |
188 char buf[1024]; | |
189 | |
190 int hasName = plink->icqlink->icq_ProxyName && | |
191 strlen(plink->icqlink->icq_ProxyName); | |
192 int hasPass = plink->icqlink->icq_ProxyPass && | |
193 strlen(plink->icqlink->icq_ProxyPass); | |
194 int authEnabled = hasName && hasPass && plink->icqlink->icq_ProxyAuth; | |
195 | |
196 plink->mode = (plink->mode & (~TCP_LINK_SOCKS_CONNECTING)); | |
197 buf[0] = 5; /* protocol version */ | |
198 buf[1] = 1; /* number of methods */ | |
199 buf[2] = authEnabled ? 2 : 0; /* authentication method */ | |
200 | |
201 plink->mode |= authEnabled ? TCP_LINK_SOCKS_AUTHORIZATION : | |
202 TCP_LINK_SOCKS_NOAUTHSTATUS; | |
203 | |
204 #ifdef _WIN32 | |
205 if(send(plink->socket, buf, 3, 0) != 3) | |
206 return errno; | |
207 #else | |
208 if(write(plink->socket, buf, 3) != 3) | |
209 return errno; | |
210 #endif | |
211 return 0; | |
212 } | |
213 | |
214 int icq_TCPLinkProxyAuthorization(icq_TCPLink *plink) | |
215 { | |
216 int res; | |
217 char buf[1024]; | |
218 | |
219 plink->mode &= ~TCP_LINK_SOCKS_AUTHORIZATION; | |
220 plink->mode |= TCP_LINK_SOCKS_AUTHSTATUS; | |
221 | |
222 #ifdef _WIN32 | |
223 res = recv(plink->socket, buf, 2, 0); | |
224 #else | |
225 res = read(plink->socket, buf, 2); | |
226 #endif | |
227 if(res != 2 || buf[0] != 5 || buf[1] != 2) /* username/password authentication*/ | |
228 { | |
229 icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Authentication method incorrect\n"); | |
230 icq_SocketDelete(plink->socket); | |
231 return -1; | |
232 } | |
233 buf[0] = 1; /* version of subnegotiation */ | |
234 buf[1] = strlen(plink->icqlink->icq_ProxyName); | |
235 memcpy(&buf[2], plink->icqlink->icq_ProxyName, buf[1]); | |
236 buf[2+buf[1]] = strlen(plink->icqlink->icq_ProxyPass); | |
237 memcpy(&buf[3+buf[1]], plink->icqlink->icq_ProxyPass, buf[2+buf[1]]); | |
238 #ifdef _WIN32 | |
239 if(send(plink->socket, buf, buf[1]+buf[2+buf[1]]+3, 0) != buf[1] + buf[2+buf[1]]+3) | |
240 return errno; | |
241 #else | |
242 if(write(plink->socket, buf, buf[1]+buf[2+buf[1]]+3) != buf[1] + buf[2+buf[1]]+3) | |
243 return errno; | |
244 #endif | |
245 return 0; | |
246 } | |
247 | |
248 int icq_TCPLinkProxyAuthStatus(icq_TCPLink *plink) | |
249 { | |
250 int res; | |
251 char buf[20]; | |
252 | |
253 plink->mode = (plink->mode & (~TCP_LINK_SOCKS_AUTHSTATUS)) | TCP_LINK_SOCKS_CROSSCONNECT; | |
254 #ifdef _WIN32 | |
255 res = recv(plink->socket, buf, 2, 0); | |
256 #else | |
257 res = read(plink->socket, buf, 2); | |
258 #endif | |
259 if(res != 2 || buf[0] != 1 || buf[1] != 0) | |
260 { | |
261 icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Authorization failure\n"); | |
262 icq_SocketDelete(plink->socket); | |
263 return -1; | |
264 } | |
265 return 0; | |
266 } | |
267 | |
268 int icq_TCPLinkProxyNoAuthStatus(icq_TCPLink *plink) | |
269 { | |
270 int res; | |
271 char buf[20]; | |
272 | |
273 plink->mode = (plink->mode & (~TCP_LINK_SOCKS_NOAUTHSTATUS)) | TCP_LINK_SOCKS_CROSSCONNECT; | |
274 #ifdef _WIN32 | |
275 res = recv(plink->socket, buf, 2, 0); | |
276 #else | |
277 res = read(plink->socket, buf, 2); | |
278 #endif | |
279 if(res != 2 || buf[0] != 5 || buf[1] != 0) /* no authentication required */ | |
280 { | |
281 icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Authentication method incorrect\n"); | |
282 icq_SocketDelete(plink->socket); | |
283 return -1; | |
284 } | |
285 return 0; | |
286 } | |
287 | |
288 int icq_TCPLinkProxyCrossConnect(icq_TCPLink *plink) | |
289 { | |
290 char buf[20]; | |
291 | |
292 plink->mode = (plink->mode & ~(TCP_LINK_SOCKS_CROSSCONNECT)) | TCP_LINK_SOCKS_CONNSTATUS; | |
293 buf[0] = 5; /* protocol version */ | |
294 buf[1] = 1; /* command connect */ | |
295 buf[2] = 0; /* reserved */ | |
296 buf[3] = 1; /* address type IP v4 */ | |
297 memcpy(&buf[4], &plink->remote_address.sin_addr.s_addr, 4); | |
298 memcpy(&buf[8], &plink->remote_address.sin_port, 2); | |
299 #ifdef _WIN32 | |
300 if(send(plink->socket, buf, 10, 0) != 10) | |
301 return errno; | |
302 #else | |
303 if(write(plink->socket, buf, 10) != 10) | |
304 return errno; | |
305 #endif | |
306 return 0; | |
307 } | |
308 | |
309 int icq_TCPLinkProxyConnectStatus(icq_TCPLink *plink) | |
310 { | |
311 int res; | |
312 char buf[1024]; | |
313 | |
314 plink->mode = (plink->mode & (~TCP_LINK_SOCKS_CONNSTATUS)); | |
315 #ifdef _WIN32 | |
316 res = recv(plink->socket, buf, 10, 0); | |
317 #else | |
318 res = read(plink->socket, buf, 10); | |
319 #endif | |
320 if(res != 10 || buf[0] != 5 || buf[1] != 0) | |
321 { | |
322 switch(buf[1]) | |
323 { | |
324 case 1: | |
325 icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] General SOCKS server failure\n"); | |
326 res = EFAULT; | |
327 break; | |
328 case 2: | |
329 icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Connection not allowed by ruleset\n"); | |
330 res = EACCES; | |
331 break; | |
332 case 3: | |
333 icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Network unreachable\n"); | |
334 res = ENETUNREACH; | |
335 break; | |
336 case 4: | |
337 icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Host unreachable\n"); | |
338 res = ENETUNREACH; | |
339 break; | |
340 case 5: | |
341 icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Connection refused\n"); | |
342 res = ECONNREFUSED; | |
343 break; | |
344 case 6: | |
345 icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] TTL expired\n"); | |
346 res = ETIMEDOUT; | |
347 break; | |
348 case 7: | |
349 icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Command not supported\n"); | |
350 res = EOPNOTSUPP; | |
351 break; | |
352 case 8: | |
353 icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Address type not supported\n"); | |
354 res = EAFNOSUPPORT; | |
355 break; | |
356 default: | |
357 icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Unknown SOCKS server failure\n"); | |
358 res = EFAULT; | |
359 break; | |
360 } | |
361 icq_SocketDelete(plink->socket); | |
362 return res; | |
363 } | |
364 return 0; | |
365 } | |
366 | |
367 int icq_TCPLinkConnect(icq_TCPLink *plink, DWORD uin, int port) | |
368 { | |
369 icq_ContactItem *pcontact=icq_ContactFind(plink->icqlink, uin); | |
370 icq_Packet *p; | |
371 int result; | |
372 | |
373 #ifndef _WIN32 | |
374 int flags; | |
375 #else | |
376 u_long iosflag; | |
377 #endif | |
378 | |
379 /* these return values never and nowhere checked */ | |
380 /* denis. */ | |
381 if(!pcontact) | |
382 return -2; | |
383 | |
384 if((plink->socket=icq_SocketNew(AF_INET, SOCK_STREAM, 0)) < 0) | |
385 return -3; | |
386 | |
387 /* bzero(&(plink->remote_address), sizeof(plink->remote_address)); Win32 incompatible... */ | |
388 memset(&(plink->remote_address), 0, sizeof(plink->remote_address)); | |
389 plink->remote_address.sin_family = AF_INET; | |
390 | |
391 /* if our IP is the same as the remote user's ip, connect to real_ip | |
392 instead since we're both probably behind a firewall */ | |
393 icq_FmtLog(plink->icqlink, ICQ_LOG_MESSAGE, | |
394 "local IP is %08X:%d, remote real IP is %08X:%d, remote IP is %08X:%d, port is %d\n", | |
395 plink->icqlink->icq_OurIP, | |
396 plink->icqlink->icq_OurPort, | |
397 pcontact->remote_real_ip, | |
398 pcontact->remote_port, | |
399 pcontact->remote_ip, | |
400 pcontact->remote_port, | |
401 port | |
402 ); | |
403 if (plink->icqlink->icq_OurIP == pcontact->remote_ip) | |
404 plink->remote_address.sin_addr.s_addr = htonl(pcontact->remote_real_ip); | |
405 else | |
406 plink->remote_address.sin_addr.s_addr = htonl(pcontact->remote_ip); | |
407 | |
408 if(plink->type==TCP_LINK_MESSAGE) | |
409 { | |
410 plink->remote_address.sin_port = htons(pcontact->remote_port); | |
411 icq_FmtLog(plink->icqlink, ICQ_LOG_MESSAGE, | |
412 "initiating message connect to %d (%s:%d)\n", uin, | |
413 inet_ntoa(*((struct in_addr *)(&(plink->remote_address.sin_addr)))), | |
414 pcontact->remote_port); | |
415 } | |
416 else | |
417 { | |
418 plink->remote_address.sin_port = htons(port); | |
419 icq_FmtLog(plink->icqlink, ICQ_LOG_MESSAGE, | |
420 "initiating file/chat connect to %d (%s:%d)\n", uin, | |
421 inet_ntoa(*((struct in_addr *)(&(plink->remote_address.sin_addr)))), | |
422 port); | |
423 } | |
424 | |
425 /* set the socket to non-blocking */ | |
426 #ifdef _WIN32 | |
427 iosflag = TRUE; | |
428 ioctlsocket(plink->socket, FIONBIO, &iosflag); | |
429 #else | |
430 flags=fcntl(plink->socket, F_GETFL, 0); | |
431 fcntl(plink->socket, F_SETFL, flags | O_NONBLOCK); | |
432 #endif | |
433 | |
434 if(!plink->icqlink->icq_UseProxy) | |
435 result=connect(plink->socket, (struct sockaddr *)&(plink->remote_address), | |
436 sizeof(plink->remote_address)); | |
437 else /* SOCKS proxy support */ | |
438 result=icq_TCPLinkProxyConnect(plink, uin, port); | |
439 /* FIXME: Here we should check for errors on connection */ | |
440 /* because of proxy support - it can't be checked */ | |
441 /* by getsockopt() later in _handle_ready_sockets() */ | |
442 /* denis. */ | |
443 | |
444 plink->mode|=TCP_LINK_MODE_CONNECTING; | |
445 | |
446 plink->remote_uin=uin; | |
447 | |
448 /* Send the hello packet */ | |
449 p=icq_TCPCreateInitPacket(plink); | |
450 icq_TCPLinkSend(plink, p); | |
451 | |
452 #ifdef TCP_PACKET_TRACE | |
453 printf("hello packet queued for %lu\n", uin); | |
454 #endif /* TCP_PACKET_TRACE */ | |
455 | |
456 icq_SocketSetHandler(plink->socket, ICQ_SOCKET_WRITE, | |
2496
f0a2a9afdb77
[gaim-migrate @ 2509]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2086
diff
changeset
|
457 (icq_SocketHandler)icq_TCPLinkOnConnect, plink); |
2086 | 458 plink->connect_timeout=icq_TimeoutNew(TCP_LINK_CONNECT_TIMEOUT, |
459 (icq_TimeoutHandler)icq_TCPLinkClose, plink); | |
460 | |
461 return 1; | |
462 } | |
463 | |
464 icq_TCPLink *icq_TCPLinkAccept(icq_TCPLink *plink) | |
465 { | |
466 #ifdef _WIN32 | |
467 u_long iosflag; | |
468 #else | |
469 int flags; | |
470 #endif | |
471 int socket_fd; | |
472 size_t remote_length; | |
473 icq_TCPLink *pnewlink=icq_TCPLinkNew(plink->icqlink); | |
474 | |
475 if(pnewlink) | |
476 { | |
477 remote_length = sizeof(struct sockaddr_in); | |
478 socket_fd=icq_SocketAccept(plink->socket, | |
479 (struct sockaddr *)&(plink->remote_address), &remote_length); | |
480 | |
481 icq_FmtLog(plink->icqlink, ICQ_LOG_MESSAGE, | |
482 "accepting tcp connection from %s:%d\n", | |
483 inet_ntoa(*((struct in_addr *)(&(plink->remote_address.sin_addr)))), | |
484 ntohs(plink->remote_address.sin_port)); | |
485 | |
486 /* FIXME: make sure accept succeeded */ | |
487 | |
488 pnewlink->type=plink->type; | |
489 pnewlink->socket=socket_fd; | |
490 | |
491 /* first packet sent on an icq tcp link is always the hello packet */ | |
492 pnewlink->mode|=TCP_LINK_MODE_HELLOWAIT; | |
493 | |
494 /* install socket handler for new socket */ | |
495 icq_SocketSetHandler(socket_fd, ICQ_SOCKET_READ, | |
2496
f0a2a9afdb77
[gaim-migrate @ 2509]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2086
diff
changeset
|
496 (icq_SocketHandler)icq_TCPLinkOnDataReceived, pnewlink); |
2086 | 497 } |
498 | |
499 /* set the socket to non-blocking */ | |
500 #ifdef _WIN32 | |
501 iosflag = TRUE; | |
502 ioctlsocket(pnewlink->socket, FIONBIO, &iosflag); | |
503 #else | |
504 flags=fcntl(pnewlink->socket, F_GETFL, 0); | |
505 fcntl(pnewlink->socket, F_SETFL, flags | O_NONBLOCK); | |
506 #endif | |
507 | |
508 return pnewlink; | |
509 } | |
510 | |
511 int icq_TCPLinkListen(icq_TCPLink *plink) | |
512 { | |
513 unsigned int t; | |
514 | |
515 /* listening links have 0 uin */ | |
516 plink->remote_uin=0; | |
517 | |
518 /* create tcp listen socket */ | |
519 if((plink->socket=icq_SocketNew(AF_INET, SOCK_STREAM, 0)) < 0) | |
520 return -1; | |
521 | |
522 /* must use memset, no bzero for Win32! */ | |
523 memset(&plink->socket_address, 0, sizeof(struct sockaddr_in)); | |
524 plink->socket_address.sin_family=AF_INET; | |
525 plink->socket_address.sin_addr.s_addr=htonl(INADDR_ANY); | |
526 plink->socket_address.sin_port=0; | |
527 | |
528 if(bind(plink->socket, (struct sockaddr *)&plink->socket_address, sizeof(struct sockaddr_in)) < 0) | |
529 return -2; | |
530 | |
531 if(listen(plink->socket, 5) < 0) | |
532 return -3; | |
533 | |
534 t=sizeof(struct sockaddr_in); | |
535 if(getsockname(plink->socket, (struct sockaddr *)&plink->socket_address, &t) < 0) | |
536 return -4; | |
537 | |
538 icq_FmtLog(plink->icqlink, ICQ_LOG_MESSAGE, | |
539 "created tcp listening socket %d, local address=%s:%d\n", | |
540 plink->socket, | |
541 inet_ntoa(*((struct in_addr *)(&plink->socket_address.sin_addr))), | |
542 ntohs(plink->socket_address.sin_port)); | |
543 | |
544 plink->mode|=TCP_LINK_MODE_LISTEN; | |
545 | |
2496
f0a2a9afdb77
[gaim-migrate @ 2509]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2086
diff
changeset
|
546 icq_SocketSetHandler(plink->socket, ICQ_SOCKET_READ, (icq_SocketHandler)icq_TCPLinkAccept, |
2086 | 547 plink); |
548 | |
549 return 0; | |
550 } | |
551 | |
552 /* Doing Cyrillic translations for Chat dialog sessions */ | |
553 void icq_ChatRusConv_n(const char to[4], char *t_in, int t_len) | |
554 { | |
555 int i, j; | |
556 | |
557 for(i = j = 0; i < t_len; ++i) | |
558 { | |
559 if((((unsigned char)t_in[i]) < ' ') && (t_in[i] != '\r')) | |
560 { | |
561 if(i - 1 > j) | |
562 icq_RusConv_n(to, &t_in[j], i - j - 1); | |
563 switch(t_in[i]) | |
564 { | |
565 case '\x07': /* Bell */ | |
566 case '\x08': /* BackSpace */ | |
567 case '\x03': /* Chat is active */ | |
568 case '\x04': /* Chat is not active */ | |
569 break; | |
570 case '\x00': /* Foregroung color (RR GG BB ?? ) */ | |
571 case '\x01': /* Background color (RR GG BB ?? ) */ | |
572 case '\x11': /* Font style change (Bold - 1, Italic - 2, Underline - 4) */ | |
573 case '\x12': /* Font size change */ | |
574 i += 4; | |
575 break; | |
576 case '\x10': /* Font family and encoding change */ | |
577 i += t_in[i+1] + 2 + 2; | |
578 icq_RusConv_n(to, &t_in[i+3], t_in[i+1]); | |
579 break; | |
580 } | |
581 j = i + 1; | |
582 } | |
583 } | |
584 if(i > t_len) | |
585 i = t_len; | |
586 if(j > t_len) | |
587 j = t_len; | |
588 if(i > j) | |
589 icq_RusConv_n(to, &t_in[j], i - j); | |
590 } | |
591 | |
592 void icq_TCPLinkOnDataReceived(icq_TCPLink *plink) | |
593 { | |
594 int process_count=0, recv_result=0; | |
595 char *buffer=plink->buffer; | |
596 | |
597 do { /* while recv_result > 0 */ | |
598 | |
599 int done=0; | |
600 | |
601 /* append received data onto end of buffer */ | |
602 if((recv_result=recv(plink->socket, buffer+plink->buffer_count, | |
603 icq_TCPLinkBufferSize-plink->buffer_count, 0)) < 1) | |
604 { | |
605 /* either there was an error or the remote side has closed | |
606 * the connection - fall out of the loop */ | |
607 continue; | |
608 }; | |
609 | |
610 plink->buffer_count+=recv_result; | |
611 | |
612 #ifdef TCP_BUFFER_TRACE | |
613 printf("received %d bytes from link %x, new buffer count %d\n", | |
614 recv_result, plink, plink->buffer_count); | |
615 | |
616 hex_dump(plink->buffer, plink->buffer_count); | |
617 #endif /*TCP_BUFFER_TRACE*/ | |
618 | |
619 process_count+=recv_result; | |
620 | |
621 /* don't do any packet processing if we're in raw mode */ | |
622 if(plink->mode & TCP_LINK_MODE_RAW) { | |
623 /* notify the app with the new data */ | |
624 if(plink->type == TCP_LINK_CHAT) | |
625 icq_ChatRusConv_n("wk", plink->buffer, plink->buffer_count); | |
626 invoke_callback(plink->icqlink, icq_ChatNotify)(plink->session, | |
627 CHAT_NOTIFY_DATA, plink->buffer_count, plink->buffer); | |
628 plink->buffer_count=0; | |
629 continue; | |
630 } | |
631 | |
632 /* remove packets from the buffer until the buffer is empty | |
633 * or the remaining bytes do not equal a full packet */ | |
634 while((unsigned)plink->buffer_count>sizeof(WORD) && !done) | |
635 { | |
636 WORD packet_size=(*((WORD *)buffer)); | |
637 | |
638 /* warn if the buffer is too small to hold the whole packet */ | |
639 if(packet_size>icq_TCPLinkBufferSize-sizeof(WORD)) | |
640 { | |
641 icq_FmtLog(plink->icqlink, ICQ_LOG_WARNING, "tcplink buffer " | |
642 "overflow, packet size = %d, buffer size = %d, closing link\n", | |
643 packet_size, icq_TCPLinkBufferSize); | |
644 return; | |
645 } | |
646 | |
647 if(packet_size+sizeof(WORD) <= (unsigned)plink->buffer_count) | |
648 { | |
649 /* copy the packet into memory */ | |
650 icq_Packet *p=icq_PacketNew(); | |
651 icq_PacketAppend(p, buffer+sizeof(WORD), packet_size); | |
652 | |
653 /* remove it from the buffer */ | |
654 memcpy(buffer, buffer+packet_size+sizeof(WORD), | |
655 plink->buffer_count-packet_size-sizeof(WORD)); | |
656 | |
657 plink->buffer_count-=(packet_size+sizeof(WORD)); | |
658 | |
659 icq_TCPLinkOnPacketReceived(plink, p); | |
660 } | |
661 else | |
662 { | |
663 /* not enough bytes in buffer to form the complete packet. | |
664 * we're done for now */ | |
665 done=1; | |
666 } | |
667 } /* while packets remain in buffer */ | |
668 | |
669 } while (recv_result > 0); | |
670 | |
671 #ifdef _WIN32 | |
672 if (recv_result <= 0 && WSAGetLastError()!=EWOULDBLOCK) { | |
673 /* receive error - log it */ | |
674 icq_FmtLog(plink->icqlink, ICQ_LOG_WARNING, "recv failed from %d (%d)," | |
675 " closing link\n", plink->remote_uin, WSAGetLastError()); | |
676 #else | |
677 if (recv_result <= 0 && errno!=EWOULDBLOCK) { | |
678 /* receive error - log it */ | |
679 icq_FmtLog(plink->icqlink, ICQ_LOG_WARNING, "recv failed from %d (%d-%s)," | |
680 " closing link\n", plink->remote_uin, errno, strerror(errno)); | |
681 #endif | |
682 | |
683 icq_TCPLinkClose(plink); | |
684 | |
685 } else { | |
686 | |
687 icq_TCPLinkProcessReceived(plink); | |
688 | |
689 } | |
690 | |
691 } | |
692 | |
693 void icq_TCPLinkOnPacketReceived(icq_TCPLink *plink, icq_Packet *p) | |
694 { | |
695 | |
696 #ifdef TCP_RAW_TRACE | |
697 printf("packet received! { length=%d }\n", p->length); | |
698 icq_PacketDump(p); | |
699 #endif | |
700 | |
701 /* Stick packet on ready packet linked icq_List */ | |
702 icq_ListEnqueue(plink->received_queue, p); | |
703 } | |
704 | |
705 void icq_TCPLinkOnConnect(icq_TCPLink *plink) | |
706 { | |
707 #ifdef _WIN32 | |
708 int len; | |
709 #else | |
710 size_t len; | |
711 #endif | |
712 int error; | |
713 | |
714 icq_TimeoutDelete(plink->connect_timeout); | |
715 plink->connect_timeout = NULL; | |
716 | |
717 /* check getsockopt */ | |
718 len=sizeof(error); | |
719 | |
720 #ifndef __BEOS__ | |
721 #ifdef _WIN32 | |
722 getsockopt(plink->socket, SOL_SOCKET, SO_ERROR, (char *)&error, &len); | |
723 #else | |
724 getsockopt(plink->socket, SOL_SOCKET, SO_ERROR, &error, &len); | |
725 #endif | |
726 #endif | |
727 if(!error && (plink->mode & (TCP_LINK_SOCKS_CONNECTING | TCP_LINK_SOCKS_AUTHORIZATION | | |
728 TCP_LINK_SOCKS_AUTHSTATUS | TCP_LINK_SOCKS_NOAUTHSTATUS | | |
729 TCP_LINK_SOCKS_CROSSCONNECT | TCP_LINK_SOCKS_CONNSTATUS))) | |
730 { | |
731 if(plink->mode & TCP_LINK_SOCKS_CONNECTING) | |
732 error = icq_TCPLinkProxyRequestAuthorization(plink); | |
733 else if(plink->mode & TCP_LINK_SOCKS_AUTHORIZATION) | |
734 error = icq_TCPLinkProxyAuthorization(plink); | |
735 else if(plink->mode & TCP_LINK_SOCKS_AUTHSTATUS) | |
736 error = icq_TCPLinkProxyAuthStatus(plink); | |
737 else if(plink->mode & TCP_LINK_SOCKS_NOAUTHSTATUS) | |
738 error = icq_TCPLinkProxyNoAuthStatus(plink); | |
739 else if(plink->mode & TCP_LINK_SOCKS_CROSSCONNECT) | |
740 error = icq_TCPLinkProxyCrossConnect(plink); | |
741 else if(plink->mode & TCP_LINK_SOCKS_CONNSTATUS) | |
742 error = icq_TCPLinkProxyConnectStatus(plink); | |
743 else | |
744 error = EINVAL; | |
745 } | |
746 | |
747 if(error) | |
748 { | |
749 /* connection failed- close the link, which takes care | |
750 * of notifying the app about packets that didn't make it */ | |
751 icq_FmtLog(plink->icqlink, ICQ_LOG_WARNING, "connect failed to %d (%d-%s)," | |
752 " closing link\n", plink->remote_uin, error, strerror(error)); | |
753 | |
754 icq_TCPLinkClose(plink); | |
755 return; | |
756 } | |
757 | |
758 if(plink->mode & (TCP_LINK_SOCKS_CONNECTING | TCP_LINK_SOCKS_AUTHORIZATION | TCP_LINK_SOCKS_AUTHSTATUS | TCP_LINK_SOCKS_NOAUTHSTATUS | TCP_LINK_SOCKS_CROSSCONNECT | TCP_LINK_SOCKS_CONNSTATUS)) | |
759 { | |
760 icq_SocketSetHandler(plink->socket, ICQ_SOCKET_WRITE, NULL, NULL); | |
761 icq_SocketSetHandler(plink->socket, ICQ_SOCKET_READ, | |
2496
f0a2a9afdb77
[gaim-migrate @ 2509]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2086
diff
changeset
|
762 (icq_SocketHandler)icq_TCPLinkOnConnect, plink); |
2086 | 763 return; |
764 } | |
765 | |
766 len=sizeof(plink->socket_address); | |
767 getsockname(plink->socket, (struct sockaddr *)&plink->socket_address, &len); | |
768 | |
769 icq_FmtLog(plink->icqlink, ICQ_LOG_MESSAGE, | |
770 "connected to uin %d, socket=%d local address=%s:%d remote address=%s:%d\n", | |
771 plink->remote_uin, plink->socket, | |
772 inet_ntoa(*((struct in_addr *)(&plink->socket_address.sin_addr))), | |
773 ntohs(plink->socket_address.sin_port), | |
774 inet_ntoa(*((struct in_addr *)(&plink->remote_address.sin_addr))), | |
775 ntohs(plink->remote_address.sin_port)); | |
776 | |
777 plink->mode&= ~TCP_LINK_MODE_CONNECTING; | |
778 | |
779 icq_SocketSetHandler(plink->socket, ICQ_SOCKET_READ, | |
2496
f0a2a9afdb77
[gaim-migrate @ 2509]
Eric Warmenhoven <eric@warmenhoven.org>
parents:
2086
diff
changeset
|
780 (icq_SocketHandler)icq_TCPLinkOnDataReceived, plink); |
2086 | 781 icq_SocketSetHandler(plink->socket, ICQ_SOCKET_WRITE, NULL, NULL); |
782 | |
783 /* socket is now connected, notify each request that connection | |
784 * has been established and send pending data */ | |
785 while(plink->send_queue->count>0) | |
786 { | |
787 icq_Packet *p=icq_ListDequeue(plink->send_queue); | |
788 if(p->id) | |
789 if(plink->icqlink->icq_RequestNotify) | |
790 (*plink->icqlink->icq_RequestNotify)(plink->icqlink, p->id, ICQ_NOTIFY_CONNECTED, 0, 0); | |
791 icq_TCPLinkSend(plink, p); | |
792 } | |
793 | |
794 /* yeah this probably shouldn't be here. oh well :) */ | |
795 if(plink->type==TCP_LINK_CHAT) | |
796 { | |
797 icq_ChatSessionSetStatus((icq_ChatSession *)plink->session, | |
798 CHAT_STATUS_CONNECTED); | |
799 icq_ChatSessionSetStatus((icq_ChatSession *)plink->session, | |
800 CHAT_STATUS_WAIT_ALLINFO); | |
801 } | |
802 | |
803 if(plink->type==TCP_LINK_FILE) | |
804 { | |
805 icq_FileSessionSetStatus((icq_FileSession *)plink->session, | |
806 FILE_STATUS_CONNECTED); | |
807 } | |
808 | |
809 } | |
810 | |
811 unsigned long icq_TCPLinkSendSeq(icq_TCPLink *plink, icq_Packet *p, | |
812 unsigned long sequence) | |
813 { | |
814 /* append the next sequence number on the packet */ | |
815 if (!sequence) | |
816 sequence=plink->icqlink->d->icq_TCPSequence--; | |
817 p->id=sequence; | |
818 icq_PacketEnd(p); | |
819 icq_PacketAppend32(p, sequence); | |
820 | |
821 /* if the link is currently connecting, queue the packets for | |
822 * later, else send immediately */ | |
823 if(plink->mode & TCP_LINK_MODE_CONNECTING) { | |
824 icq_ListInsert(plink->send_queue, 0, p); | |
825 if(plink->icqlink->icq_RequestNotify) | |
826 (*plink->icqlink->icq_RequestNotify)(plink->icqlink, p->id, ICQ_NOTIFY_CONNECTING, 0, 0); | |
827 } else { | |
828 icq_PacketSend(p, plink->socket); | |
829 if(p->id) | |
830 if(plink->icqlink->icq_RequestNotify) | |
831 (*plink->icqlink->icq_RequestNotify)(plink->icqlink, p->id, ICQ_NOTIFY_SENT, 0, 0); | |
832 icq_PacketDelete(p); | |
833 } | |
834 return sequence; | |
835 } | |
836 | |
837 void icq_TCPLinkSend(icq_TCPLink *plink, icq_Packet *p) | |
838 { | |
839 /* if the link is currently connecting, queue the packets for | |
840 * later, else send immediately */ | |
841 if(plink->mode & TCP_LINK_MODE_CONNECTING) { | |
842 icq_ListInsert(plink->send_queue, 0, p); | |
843 if(plink->icqlink->icq_RequestNotify) | |
844 (*plink->icqlink->icq_RequestNotify)(plink->icqlink, p->id, ICQ_NOTIFY_CONNECTING, 0, 0); | |
845 } else { | |
846 icq_PacketSend(p, plink->socket); | |
847 if(p->id) | |
848 if(plink->icqlink->icq_RequestNotify) | |
849 (*plink->icqlink->icq_RequestNotify)(plink->icqlink, p->id, ICQ_NOTIFY_SENT, 0, 0); | |
850 icq_PacketDelete(p); | |
851 } | |
852 } | |
853 | |
854 void icq_TCPLinkProcessReceived(icq_TCPLink *plink) | |
855 { | |
856 icq_List *plist=plink->received_queue; | |
857 while(plist->count>0) | |
858 | |
859 { | |
860 icq_Packet *p=icq_ListDequeue(plist); | |
861 | |
862 if(plink->mode & TCP_LINK_MODE_HELLOWAIT) | |
863 { | |
864 icq_TCPProcessHello(p, plink); | |
865 } | |
866 else | |
867 { | |
868 | |
869 switch (plink->type) { | |
870 | |
871 case TCP_LINK_MESSAGE: | |
872 icq_TCPProcessPacket(p, plink); | |
873 break; | |
874 | |
875 case TCP_LINK_CHAT: | |
876 icq_TCPProcessChatPacket(p, plink); | |
877 break; | |
878 | |
879 case TCP_LINK_FILE: | |
880 icq_TCPProcessFilePacket(p, plink); | |
881 break; | |
882 | |
883 } | |
884 } | |
885 | |
886 icq_PacketDelete(p); | |
887 } | |
888 | |
889 } | |
890 | |
891 int _icq_FindTCPLink(void *p, va_list data) | |
892 { | |
893 icq_TCPLink *plink=(icq_TCPLink *)p; | |
894 unsigned long uin=va_arg(data, unsigned long); | |
895 int type=va_arg(data, int); | |
896 | |
897 return ( (plink->remote_uin == uin ) && (plink->type == type) ); | |
898 } | |
899 | |
900 icq_TCPLink *icq_FindTCPLink(icq_Link *icqlink, unsigned long uin, int type) | |
901 { | |
902 return icq_ListTraverse(icqlink->d->icq_TCPLinks, _icq_FindTCPLink, uin, type); | |
903 } |