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