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