1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /*
3 $Id: tcp.c,v 1.36 2000/07/09 22:19:35 bills Exp $
4 $Log: tcp.c,v $
5 Revision 1.36 2000/07/09 22:19:35 bills
6 added new *Close functions, use *Close functions instead of *Delete
7 where correct, and misc cleanup
8
9 Revision 1.35 2000/06/15 01:52:16 bills
10 added Cancel and Refuse functions for chat and file reqs, changed packet
11 sending code to use new icq_TCPLinkSendSeq function to elimitane duplicate
12 code, removed *Seq functions, renamed chat req functions
13
14 Revision 1.34 2000/05/04 15:57:20 bills
15 Reworked file transfer notification, small bugfixes, and cleanups.
16
17 Revision 1.33 2000/04/10 18:11:45 denis
18 ANSI cleanups.
19
20 Revision 1.32 2000/04/10 16:36:04 denis
21 Some more Win32 compatibility from Guillaume Rosanis <grs@mail.com>
22
23 Revision 1.31 2000/04/06 16:38:04 denis
24 icq_*Send*Seq() functions with specified sequence number were added.
25
26 Revision 1.30 2000/04/05 14:37:02 denis
27 Applied patch from "Guillaume R." <grs@mail.com> for basic Win32
28 compatibility.
29
30 Revision 1.29 2000/02/15 04:02:41 bills
31 warning cleanup
32
33 Revision 1.28 2000/02/15 03:58:20 bills
34 use new icq_ChatRusConv_n function in icq_TCPSendChatData,
35 new icq_TCPSendChatData_n function
36
37 Revision 1.27 2000/02/07 02:40:23 bills
38 new code for SOCKS connections, more cyrillic translations
39
40 Revision 1.26 2000/01/20 19:59:15 bills
41 first implementation of sending file requests
42
43 Revision 1.25 2000/01/16 21:28:24 bills
44 renamed icq_TCPAcceptFileReq to icq_AcceptFileRequest, moved file request
45 functions to new file session code
46
47 Revision 1.24 2000/01/16 03:59:10 bills
48 reworked list code so list_nodes don't need to be inside item structures,
49 removed strlist code and replaced with generic list calls
50
51 Revision 1.23 1999/12/27 16:10:04 bills
52 fixed buy in icq_TCPAcceptFileReq, added icq_TCPFileSetSpeed
53
54 Revision 1.22 1999/12/21 00:29:59 bills
55 moved _process_packet logic into tcplink::icq_TCPLinkProcessReceived,
56 removed unnecessary icq_TCPSendFile??Packet functions
57
58 Revision 1.21 1999/12/14 03:31:48 bills
59 fixed double delete bug in _handle_ready_sockets, added code to implement
60 connect timeout
61
62 Revision 1.20 1999/11/30 09:44:31 bills
63 added file session logic
64
65 Revision 1.19 1999/09/29 20:07:12 bills
66 cleanups, moved connect logic from _handle_ready_sockets to
67 icq_TCPLinkOnConnect, tcp_link->icq_TCPLink
68
69 Revision 1.18 1999/09/29 17:08:48 denis
70 Cleanups.
71
72 Revision 1.17 1999/07/18 20:19:56 bills
73 added better log messages
74
75 Revision 1.16 1999/07/16 15:45:56 denis
76 Cleaned up.
77
78 Revision 1.15 1999/07/16 12:14:13 denis
79 tcp_packet* functions renamed to icq_Packet*
80 Cleaned up.
81
82 Revision 1.14 1999/07/12 15:13:34 cproch
83 - added definition of ICQLINK to hold session-specific global variabled
84 applications which have more than one connection are now possible
85 - changed nearly every function defintion to support ICQLINK parameter
86
87 Revision 1.13 1999/07/03 06:33:49 lord
88 . byte order conversion macros added
89 . some compilation warnings removed
90
91 Revision 1.12 1999/06/30 13:52:22 bills
92 implemented non-blocking connects
93
94 Revision 1.11 1999/05/03 21:41:26 bills
95 initial file xfer support added- untested
96
97 Revision 1.10 1999/04/29 09:35:41 denis
98 Cleanups, warning removed
99
100 Revision 1.9 1999/04/17 19:30:50 bills
101 _major_ restructuring. all tcp sockets (including listening sockets) are
102 kept in global linked list, icq_TCPLinks. accept and listen functions
103 moved to tcplink.c. changed return values of Send* functions to DWORD.
104
105 Revision 1.8 1999/04/14 14:57:05 denis
106 Cleanups for "strict" compiling (-ansi -pedantic)
107 Parameter port added to function icq_TCPCreateListeningSocket()
108
109 */
110
111 /*
112 Peer-to-peer ICQ protocol implementation
113
114 Uses version 2 of the ICQ protocol
115
116 Thanks to Douglas F. McLaughlin and many others for
117 packet details (see tcp02.txt)
118
119 */
120
121 #include <stdlib.h>
122
123 #ifndef _WIN32
124 #include <unistd.h>
125 #endif
126
127 #include <fcntl.h>
128 #include <stdarg.h>
129 #include <errno.h>
130
131 #include <sys/types.h>
132
133 #ifdef _WIN32
134 #include <winsock.h>
135 #else
136 #include <sys/socket.h>
137 #endif
138
139 #include <sys/stat.h>
140
141 #ifndef _WIN32
142 #include <sys/time.h>
143 #endif
144
145 #include "icqtypes.h"
146 #include "icqlib.h"
147
148 #include "tcp.h"
149 #include "stdpackets.h"
150 #include "list.h"
151 #include "tcplink.h"
152 #include "chatsession.h"
153 #include "filesession.h"
154
155 /**
156 Initializes structures necessary for TCP use. Not required by user
157 programs.
158
159 \return true on error
160 */
161
icq_TCPInit(ICQLINK * link)162 int icq_TCPInit(ICQLINK *link)
163 {
164 icq_TCPLink *plink;
165
166 /* allocate lists */
167 link->icq_TCPLinks=list_new();
168 link->icq_ChatSessions=list_new();
169 link->icq_FileSessions=list_new();
170
171 /* only the main listening socket gets created upon initialization -
172 * the other two are created when necessary */
173 plink=icq_TCPLinkNew( link );
174 icq_TCPLinkListen(plink);
175 link->icq_TCPSrvPort=ntohs(plink->socket_address.sin_port);
176
177 /* reset tcp sequence number */
178 link->icq_TCPSequence=0xfffffffe;
179
180 return 0;
181 }
182
icq_TCPDone(ICQLINK * link)183 void icq_TCPDone(ICQLINK *link)
184 {
185 /* close and deallocate all tcp links, this will also close any attached
186 * file or chat sessions */
187 list_delete(link->icq_TCPLinks, icq_TCPLinkDelete);
188 list_delete(link->icq_ChatSessions, icq_ChatSessionDelete);
189 list_delete(link->icq_FileSessions, icq_FileSessionDelete);
190 }
191
192 /* helper function for icq_TCPMain */
_generate_fds(void * p,va_list data)193 int _generate_fds(void *p, va_list data)
194 {
195 icq_TCPLink *plink=(icq_TCPLink *)p;
196 ICQLINK *icqlink = plink->icqlink;
197
198 (void)data;
199
200 if(plink->socket>-1)
201 {
202 int socket=plink->socket;
203
204 FD_SET(socket, &icqlink->TCP_readfds);
205
206 /* we only care about writing if socket is trying to connect */
207 if(plink->mode & TCP_LINK_MODE_CONNECTING)
208 {
209 if(plink->mode & (TCP_LINK_SOCKS_AUTHORIZATION | TCP_LINK_SOCKS_NOAUTHSTATUS | TCP_LINK_SOCKS_AUTHSTATUS | TCP_LINK_SOCKS_CONNSTATUS))
210 FD_SET(socket, &icqlink->TCP_readfds);
211 else
212 FD_SET(socket, &icqlink->TCP_writefds);
213 }
214
215 if(socket+1>icqlink->TCP_maxfd)
216 icqlink->TCP_maxfd=socket+1;
217 }
218
219 return 0; /* traverse the entire list */
220 }
221
222 /* helper function for icq_TCPMain */
_handle_ready_sockets(void * p,va_list data)223 int _handle_ready_sockets(void *p, va_list data)
224 {
225 icq_TCPLink *plink=(icq_TCPLink *)p;
226 ICQLINK *icqlink = plink->icqlink;
227 int socket=plink->socket;
228
229 (void)data;
230
231 /* handle connecting sockets */
232 if (plink->mode & TCP_LINK_MODE_CONNECTING)
233 {
234 if(socket>-1 && (FD_ISSET(socket, &icqlink->TCP_writefds) || FD_ISSET(socket, &icqlink->TCP_readfds)))
235 {
236 icq_TCPLinkOnConnect(plink);
237 return 0;
238 }
239
240 if((time(0L) - plink->connect_time) > TCP_LINK_CONNECT_TIMEOUT)
241 {
242 icq_TCPLinkClose(plink);
243 return 0;
244 }
245 }
246
247 /* handle ready for read sockets- either a connection is waiting on *
248 * the listen sockets or data is ready to be read */
249 if(socket>-1 && FD_ISSET(socket, &icqlink->TCP_readfds))
250 {
251 if(plink->mode & TCP_LINK_MODE_LISTEN)
252 (void)icq_TCPLinkAccept(plink);
253 else {
254
255 int result=icq_TCPLinkOnDataReceived(plink);
256
257 /* close the link if there was a receive error or if *
258 * the remote end has closed the connection */
259 if (result < 1)
260 icq_TCPLinkClose(plink);
261
262 }
263 }
264
265 return 0; /* traverse the entire list */
266 }
267
268 /* helper function for icq_TCPMain */
_process_links(void * p,va_list data)269 int _process_links(void *p, va_list data)
270 {
271 icq_TCPLink *plink=(icq_TCPLink *)p;
272
273 (void)data;
274
275 /* receive any packets watiting on the link */
276 icq_TCPLinkProcessReceived(plink);
277
278 /* if this a currently sending file link, send data! */
279 if(plink->type==TCP_LINK_FILE) {
280 icq_FileSession *psession=plink->session;
281 if(psession && psession->status==FILE_STATUS_SENDING)
282 icq_FileSessionSendData(psession);
283 }
284
285 return 0; /* traverse entire list */
286 }
287
icq_TCPMain(ICQLINK * link)288 void icq_TCPMain(ICQLINK *link)
289 {
290 struct timeval tv;
291
292 tv.tv_sec = 0;
293 tv.tv_usec = 0;
294
295 link->TCP_maxfd = 0;
296 FD_ZERO(&link->TCP_readfds);
297 FD_ZERO(&link->TCP_writefds);
298
299 /* generate the fd sets for all open tcp links */
300 (void)list_traverse(link->icq_TCPLinks, _generate_fds);
301
302 /* determine which sockets require maintenance */
303 select(link->TCP_maxfd, &link->TCP_readfds, &link->TCP_writefds, 0, &tv);
304
305 /* call icq_TCPLinkOnDataReceived for any sockets with ready data,
306 * send all packets on send queue if socket has connected, and
307 * accept() from any listening sockets with pending connections */
308 (void)list_traverse(link->icq_TCPLinks, _handle_ready_sockets, 0, 0);
309
310 /* process all packets waiting for each TCPLink */
311 (void)list_traverse(link->icq_TCPLinks, _process_links, 0, 0);
312 }
313
icq_TCPCheckLink(ICQLINK * link,DWORD uin,int type)314 icq_TCPLink *icq_TCPCheckLink(ICQLINK *link, DWORD uin, int type)
315 {
316 icq_TCPLink *plink=icq_FindTCPLink(link, uin, type);
317
318 if(!plink)
319 {
320 plink=icq_TCPLinkNew( link );
321 if(type==TCP_LINK_MESSAGE)
322 icq_TCPLinkConnect(plink, uin, 0);
323 }
324
325 return plink;
326
327 }
328
icq_TCPSendMessage(ICQLINK * link,DWORD uin,const char * message)329 DWORD icq_TCPSendMessage(ICQLINK *link, DWORD uin, const char *message)
330 {
331 icq_TCPLink *plink;
332 icq_Packet *p;
333 DWORD sequence;
334 char data[512] ;
335
336 strncpy(data,message,512) ;
337 icq_RusConv("kw", data) ;
338
339 plink=icq_TCPCheckLink(link, uin, TCP_LINK_MESSAGE);
340
341 /* create and send the message packet */
342 p=icq_TCPCreateMessagePacket(plink, (unsigned char *)data);
343 sequence=icq_TCPLinkSendSeq(plink, p, 0);
344
345 #ifdef TCP_PACKET_TRACE
346 printf("message packet sent to uin %lu { sequence=%lx }\n", uin, p->id);
347 #endif
348
349 return sequence;
350 }
351
icq_TCPSendURL(ICQLINK * link,DWORD uin,const char * message,const char * url)352 DWORD icq_TCPSendURL(ICQLINK *link, DWORD uin, const char *message, const char *url)
353 {
354 icq_TCPLink *plink;
355 icq_Packet *p;
356 DWORD sequence;
357 char data[512];
358
359 plink=icq_TCPCheckLink(link, uin, TCP_LINK_MESSAGE);
360
361 strncpy(data, message, 512);
362 data[511] = '\0';
363 icq_RusConv("kw", data);
364
365 /* create and send the url packet */
366 p=icq_TCPCreateURLPacket(plink, data, url);
367 sequence=icq_TCPLinkSendSeq(plink, p, 0);
368
369 #ifdef TCP_PACKET_TRACE
370 printf("url packet queued for uin %lu { sequence=%lx }\n", uin, p->id);
371 #endif
372
373 return sequence;
374 }
375
icq_SendChatRequest(ICQLINK * link,DWORD uin,const char * message)376 DWORD icq_SendChatRequest(ICQLINK *link, DWORD uin, const char *message)
377 {
378 icq_TCPLink *plink;
379 icq_Packet *p;
380 DWORD sequence;
381 char data[512];
382
383 plink=icq_TCPCheckLink(link, uin, TCP_LINK_MESSAGE);
384
385 strncpy(data, message, 512);
386 data[511] = '\0';
387 icq_RusConv("kw", data);
388
389 /* create and send the url packet */
390 p=icq_TCPCreateChatReqPacket(plink, (unsigned char *)data);
391 sequence=icq_TCPLinkSendSeq(plink, p, 0);
392
393 #ifdef TCP_PACKET_TRACE
394 printf("chat req packet sent to uin %lu { sequence=%lx }\n", uin, p->id);
395 #endif
396
397 return sequence;
398 }
399
icq_SendFileRequest(ICQLINK * link,unsigned long uin,const char * message,char ** files)400 unsigned long icq_SendFileRequest(ICQLINK *link, unsigned long uin,
401 const char *message, char **files)
402 {
403 icq_TCPLink *plink;
404 icq_FileSession *pfile;
405 icq_Packet *p;
406 unsigned long sequence;
407 char filename[64];
408 char data[512];
409
410 plink=icq_TCPCheckLink(link, uin, TCP_LINK_MESSAGE);
411
412 /* create the file session, this will be linked to the incoming icq_TCPLink
413 * in icq_HandleFileAck */
414 pfile=icq_FileSessionNew(link);
415 pfile->remote_uin=uin;
416 pfile->files=files;
417 pfile->direction=FILE_STATUS_SENDING;
418
419 /* count the number and size of the files */
420 pfile->total_files=0;
421 while(*files) {
422 struct stat file_status;
423
424 if(stat(*files, &file_status)==0) {
425 pfile->total_files++;
426 pfile->total_bytes+=file_status.st_size;
427 }
428 files++;
429 }
430
431 strncpy(filename, *(pfile->files), 64);
432
433 strncpy(data, message, 512);
434 data[511] = '\0';
435 icq_RusConv("kw", data);
436
437 /* create and send the file req packet */
438 p=icq_TCPCreateFileReqPacket(plink, (char *)data, filename,
439 pfile->total_bytes);
440 sequence=icq_TCPLinkSendSeq(plink, p, 0);
441 pfile->id=sequence;
442
443 #ifdef TCP_PACKET_TRACE
444 printf("file req packet sent to uin %lu { sequence=%lx }\n", uin, p->id);
445 #endif
446
447 return sequence;
448 }
449
icq_AcceptChatRequest(ICQLINK * link,DWORD uin,unsigned long sequence)450 void icq_AcceptChatRequest(ICQLINK *link, DWORD uin, unsigned long sequence)
451 {
452 icq_TCPLink *pmessage, *plisten;
453 icq_ChatSession *pchat;
454 icq_Packet *p;
455
456 pmessage=icq_TCPCheckLink(link, uin, TCP_LINK_MESSAGE);
457
458 /* create the chat listening socket if necessary */
459 if(!(plisten=icq_FindTCPLink(link, 0, TCP_LINK_CHAT)))
460 {
461 plisten=icq_TCPLinkNew( link );
462 plisten->type=TCP_LINK_CHAT;
463 icq_TCPLinkListen(plisten);
464 }
465
466 /* create the chat session, this will be linked to the incoming icq_TCPLink
467 * in TCPProcessHello */
468 pchat=icq_ChatSessionNew(link);
469 pchat->id=sequence;
470 pchat->remote_uin=uin;
471
472 /* create and send the ack packet */
473 p=icq_TCPCreateChatReqAck(pmessage,
474 ntohs(plisten->socket_address.sin_port));
475 (void)icq_TCPLinkSendSeq(pmessage, p, sequence);
476
477 #ifdef TCP_PACKET_TRACE
478 printf("chat req ack sent to uin %lu { sequence=%lx }\n", uin, sequence);
479 #endif
480 }
481
icq_TCPSendChatData(ICQLINK * link,DWORD uin,const char * data)482 void icq_TCPSendChatData(ICQLINK *link, DWORD uin, const char *data)
483 {
484 icq_TCPLink *plink=icq_FindTCPLink(link, uin, TCP_LINK_CHAT);
485 char data1[512];
486 int data1_len;
487
488 if(!plink)
489 return;
490
491 strncpy(data1,data,512) ;
492 data1[511] = '\0';
493 data1_len = strlen(data);
494 icq_ChatRusConv_n("kw", data1, data1_len);
495
496 send(plink->socket, data1, data1_len, 0);
497
498 }
499
icq_TCPSendChatData_n(ICQLINK * link,DWORD uin,const char * data,int len)500 void icq_TCPSendChatData_n(ICQLINK *link, DWORD uin, const char *data, int len)
501 {
502 icq_TCPLink *plink=icq_FindTCPLink(link, uin, TCP_LINK_CHAT);
503 char *data1;
504
505 if(!plink)
506 return;
507
508 data1 = (char *)malloc(len);
509 memcpy(data1, data, len);
510 icq_ChatRusConv_n("kw", data1, len);
511
512 send(plink->socket, data1, len, 0);
513
514 }
515
icq_TCPCloseChat(ICQLINK * link,unsigned long uin)516 void icq_TCPCloseChat(ICQLINK *link, unsigned long uin)
517 {
518 icq_TCPLink *plink=icq_FindTCPLink(link, uin, TCP_LINK_CHAT);
519
520 if(plink)
521 icq_TCPLinkClose(plink);
522
523 }
524
icq_AcceptFileRequest(ICQLINK * link,DWORD uin,unsigned long sequence)525 icq_FileSession *icq_AcceptFileRequest(ICQLINK *link, DWORD uin,
526 unsigned long sequence)
527 {
528 icq_TCPLink *pmessage, *plisten;
529 icq_FileSession *pfile;
530 icq_Packet *p;
531
532 pmessage=icq_TCPCheckLink(link, uin, TCP_LINK_MESSAGE);
533
534 /* create the file listening socket if necessary */
535 if(!(plisten=icq_FindTCPLink(link, 0, TCP_LINK_FILE)))
536 {
537 plisten=icq_TCPLinkNew( link );
538 plisten->type=TCP_LINK_FILE;
539 icq_TCPLinkListen(plisten);
540 }
541
542 /* create the file session, this will be linked to the incoming icq_TCPLink
543 * in TCPProcessHello */
544 pfile=icq_FileSessionNew(link);
545 pfile->id=sequence;
546 pfile->remote_uin=uin;
547 pfile->direction=FILE_STATUS_RECEIVING;
548 icq_FileSessionSetStatus(pfile, FILE_STATUS_LISTENING);
549
550 /* create and send the ack packet */
551 p=icq_TCPCreateFileReqAck(pmessage,
552 ntohs(plisten->socket_address.sin_port));
553 (void)icq_TCPLinkSendSeq(pmessage, p, sequence);
554
555 #ifdef TCP_PACKET_TRACE
556 printf("file req ack sent to uin %lu { sequence=%lx }\n", uin, sequence);
557 #endif
558
559 return pfile;
560
561 }
562
icq_RefuseFileRequest(ICQLINK * link,DWORD uin,unsigned long sequence,const char * reason)563 void icq_RefuseFileRequest(ICQLINK *link, DWORD uin,
564 unsigned long sequence, const char *reason)
565 {
566 icq_TCPLink *pmessage=icq_TCPCheckLink(link, uin, TCP_LINK_MESSAGE);
567 icq_Packet *p;
568
569 /* create and send the refuse packet */
570 p=icq_TCPCreateFileReqRefuse(pmessage,
571 ntohs(pmessage->socket_address.sin_port), reason);
572 (void)icq_TCPLinkSendSeq(pmessage, p, sequence);
573
574 #ifdef TCP_PACKET_TRACE
575 printf("file req refuse sent to uin %lu { sequence=%lx, reason=\"%s\" }\n",
576 uin, sequence, reason);
577 #endif
578
579 }
580
icq_CancelFileRequest(ICQLINK * link,DWORD uin,unsigned long sequence)581 void icq_CancelFileRequest(ICQLINK *link, DWORD uin, unsigned long sequence)
582 {
583 icq_TCPLink *pmessage=icq_TCPCheckLink(link, uin, TCP_LINK_MESSAGE);
584 icq_FileSession *psession=icq_FindFileSession(link, uin, sequence);
585 icq_Packet *p;
586
587 if (psession)
588 icq_FileSessionClose(psession);
589
590 /* create and send the cancel packet */
591 p=icq_TCPCreateFileReqCancel(pmessage,
592 ntohs(pmessage->socket_address.sin_port));
593 (void)icq_TCPLinkSendSeq(pmessage, p, sequence);
594 #ifdef TCP_PACKET_TRACE
595 printf("file req cancel sent to uin %lu { sequence=%lx }\n", uin, sequence);
596 #endif
597
598 }
599
icq_RefuseChatRequest(ICQLINK * link,DWORD uin,unsigned long sequence,const char * reason)600 void icq_RefuseChatRequest(ICQLINK *link, DWORD uin,
601 unsigned long sequence, const char *reason)
602 {
603 icq_TCPLink *pmessage=icq_TCPCheckLink(link, uin, TCP_LINK_MESSAGE);
604 icq_Packet *p;
605
606 /* create and send the refuse packet */
607 p=icq_TCPCreateChatReqRefuse(pmessage,
608 ntohs(pmessage->socket_address.sin_port), reason);
609 (void)icq_TCPLinkSendSeq(pmessage, p, sequence);
610
611 #ifdef TCP_PACKET_TRACE
612 printf("chat req refuse sent to uin %lu { sequence=%lx, reason=\"%s\" }\n",
613 uin, sequence, reason);
614 #endif
615
616 }
617
icq_CancelChatRequest(ICQLINK * link,DWORD uin,unsigned long sequence)618 void icq_CancelChatRequest(ICQLINK *link, DWORD uin, unsigned long sequence)
619 {
620 icq_TCPLink *pmessage=icq_TCPCheckLink(link, uin, TCP_LINK_MESSAGE);
621 icq_FileSession *psession=icq_FindFileSession(link, uin, sequence);
622 icq_Packet *p;
623
624 if (psession)
625 icq_FileSessionClose(psession);
626
627 /* create and send the cancel packet */
628 p=icq_TCPCreateChatReqCancel(pmessage,
629 ntohs(pmessage->socket_address.sin_port));
630 (void)icq_TCPLinkSendSeq(pmessage, p, sequence);
631
632 #ifdef TCP_PACKET_TRACE
633 printf("chat req cancel sent to uin %lu { sequence=%lx }\n", uin, sequence);
634 #endif
635
636 }
637