| |
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 } |