Mercurial > pidgin.yaz
view plugins/icq/tcplink.c @ 1401:bf041349b11e
[gaim-migrate @ 1411]
abliity to set accounts away independent of each other. also allows for all the other states (like in yahoo and icq). probably breaks MSN, so don't use it until rob fixes it.
committer: Tailor Script <tailor@pidgin.im>
author | Eric Warmenhoven <eric@warmenhoven.org> |
---|---|
date | Wed, 10 Jan 2001 22:15:24 +0000 |
parents | 0a766047b4fd |
children | 4c510ca3563f |
line wrap: on
line source
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* $Id: tcplink.c 1319 2000-12-19 10:08:29Z warmenhoven $ $Log$ Revision 1.2 2000/12/19 10:08:29 warmenhoven Yay, new icqlib Revision 1.40 2000/12/19 06:00:07 bills moved members from ICQLINK to ICQLINK_private struct Revision 1.39 2000/12/03 21:57:15 bills fixed bug #105068 Revision 1.38 2000/07/09 22:19:35 bills added new *Close functions, use *Close functions instead of *Delete where correct, and misc cleanup Revision 1.37 2000/06/15 01:53:17 bills added icq_TCPLinkSendSeq function Revision 1.36 2000/05/04 15:57:20 bills Reworked file transfer notification, small bugfixes, and cleanups. Revision 1.35 2000/05/03 18:29:15 denis Callbacks have been moved to the ICQLINK structure. Revision 1.34 2000/04/10 18:11:45 denis ANSI cleanups. Revision 1.33 2000/04/10 16:36:04 denis Some more Win32 compatibility from Guillaume Rosanis <grs@mail.com> Revision 1.32 2000/04/05 14:37:02 denis Applied patch from "Guillaume R." <grs@mail.com> for basic Win32 compatibility. Revision 1.31 2000/02/15 04:00:16 bills new icq_ChatRusConv_n function Revision 1.30 2000/02/07 02:46:29 bills implemented non-blocking TCP connects over SOCKS, cyrillic translation for chat Revision 1.29 2000/01/20 19:59:35 bills fixed bug in icq_TCPLinkConnect that caused file/chat connection attempts to go to the wrong port Revision 1.28 2000/01/16 03:59:10 bills reworked list code so list_nodes don't need to be inside item structures, removed strlist code and replaced with generic list calls Revision 1.27 1999/12/27 16:13:29 bills fixed bug in icq_TCPOnDataReceived, removed flags variable ;) Revision 1.26 1999/12/27 10:56:10 denis Unused "flags" variable commented out. Revision 1.25 1999/12/21 00:30:53 bills added icq_TCPLinkProcessReceived to support processing receive queue before delete (packets used to get dropped in this instance, oops), reworked icq_TCPLinkOnDataReceived to handle quick, large streams of data, changed icq_TCPLinkOnConnect and *Accept to make all sockets non-blocking. Revision 1.24 1999/12/14 03:33:34 bills icq_TCPLinkConnect now uses real_ip when our IP is same as remote IP to make connection, added code to implement connect timeout Revision 1.23 1999/11/30 09:57:44 bills buffer overflow check added, tcplinks will now close if buffer overflows. increased icq_TCPLinkBufferSize to 4096 to support file transfer packets Revision 1.22 1999/11/29 17:15:51 denis Absence of socklen_t type fixed. Revision 1.21 1999/10/01 00:49:21 lord some compilation problems are fixed. Revision 1.20 1999/09/29 20:26:41 bills ack forgot the args :) Revision 1.19 1999/09/29 20:21:45 bills renamed denis' new function Revision 1.18 1999/09/29 20:11:29 bills renamed tcp_link* to icq_TCPLink*. many cleanups, added icq_TCPLinkOnConnect Revision 1.17 1999/09/29 17:10:05 denis TCP code SOCKS-ification. Not finished. Revision 1.16 1999/07/18 20:21:34 bills fixed fail notification bug introduced during ICQLINK changes, changed to use new byte-order functions & contact list functions, added better log messages Revision 1.15 1999/07/16 15:45:57 denis Cleaned up. Revision 1.14 1999/07/16 12:02:58 denis tcp_packet* functions renamed to icq_Packet* Cleaned up. Revision 1.13 1999/07/12 15:13:36 cproch - added definition of ICQLINK to hold session-specific global variabled applications which have more than one connection are now possible - changed nearly every function defintion to support ICQLINK parameter Revision 1.12 1999/07/03 06:33:51 lord . byte order conversion macros added . some compilation warnings removed Revision 1.11 1999/06/30 13:52:23 bills implemented non-blocking connects Revision 1.10 1999/05/03 21:39:41 bills removed exit calls Revision 1.9 1999/04/29 09:35:54 denis Cleanups, warning removed Revision 1.8 1999/04/17 19:34:49 bills fixed bug in icq_TCPLinkOnDataReceived, multiple packets that come in on one recv call are now handled correctly. added icq_TCPLinkAccept and icq_TCPLinkListen. started using mode and type structure entries. added icq_TCPLinks list and icq_FindTCPLink function. changed received_queue and send_queue to lists. Revision 1.7 1999/04/14 15:02:45 denis Cleanups for "strict" compiling (-ansi -pedantic) Revision 1.6 1999/04/05 18:47:17 bills initial chat support implemented Revision 1.5 1999/03/31 01:50:54 bills wrapped up many tcp details- tcp code now handles incoming and outgoing tcp messages and urls! Revision 1.4 1999/03/28 03:27:49 bills fixed icq_TCPLinkConnect so it really connects to remote ip instead of always my local test computer O:) Revision 1.3 1999/03/26 20:02:41 bills fixed C++ comments, cleaned up Revision 1.2 1999/03/25 22:21:59 bills added necessary includes Revision 1.1 1999/03/25 21:09:07 bills tcp link functions */ #include <stdlib.h> #ifndef _WIN32 #include <unistd.h> #endif #include <fcntl.h> #include <stdarg.h> #include <errno.h> #include <sys/types.h> #ifdef _WIN32 #include <winsock.h> #define EINPROGRESS WSAEINPROGRESS #define ENETUNREACH WSAENETUNREACH #define ECONNREFUSED WSAECONNREFUSED #define ETIMEDOUT WSAETIMEDOUT #define EOPNOTSUPP WSAEOPNOTSUPP #define EAFNOSUPPORT WSAEAFNOSUPPORT #define EWOULDBLOCK WSAEWOULDBLOCK #else #include <sys/socket.h> #include <netdb.h> #endif #include "icqtypes.h" #include "icq.h" #include "icqlib.h" #include "tcplink.h" #include "stdpackets.h" #include "util.h" #include "tcp.h" #include "errno.h" #include "chatsession.h" #include "filesession.h" icq_TCPLink *icq_TCPLinkNew(ICQLINK *link) { icq_TCPLink *p=(icq_TCPLink *)malloc(sizeof(icq_TCPLink)); p->socket=-1; p->icqlink=link; p->mode=0; p->session=0L; p->type=TCP_LINK_MESSAGE; p->buffer_count=0; p->send_queue=list_new(); p->received_queue=list_new(); p->id=0; p->remote_uin=0; p->remote_version=0; p->flags=0; p->proxy_status = 0; if(p) list_enqueue(link->d->icq_TCPLinks, p); return p; } int _icq_TCPLinkDelete(void *pv, va_list data) { icq_Packet *p=(icq_Packet *)pv; ICQLINK *icqlink=va_arg(data, ICQLINK *); /* notify the app the packet didn't make it */ if(p->id) (*icqlink->icq_RequestNotify)(icqlink, p->id, ICQ_NOTIFY_FAILED, 0, 0); return 0; } void icq_TCPLinkDelete(void *pv) { icq_TCPLink *p=(icq_TCPLink *)pv; /* process anything left in the received queue */ icq_TCPLinkProcessReceived(p); /* make sure we notify app that packets in send queue didn't make it */ (void)list_traverse(p->send_queue, _icq_TCPLinkDelete, p->icqlink); /* destruct all packets still waiting on queues */ list_delete(p->send_queue, icq_PacketDelete); list_delete(p->received_queue, icq_PacketDelete); /* notify app if this was a chat or file xfer session and there is an * id assigned */ if((p->type==TCP_LINK_CHAT || p->type==TCP_LINK_FILE) && p->id) if(p->icqlink->icq_RequestNotify) (*p->icqlink->icq_RequestNotify)(p->icqlink, p->id, ICQ_NOTIFY_SUCCESS, 0, 0); /* if this is a chat or file link, delete the associated session as * well, but make sure we unassociate ourself first so the session * doesn't try to close us */ if(p->session) { if(p->type==TCP_LINK_CHAT) { icq_ChatSession *psession=p->session; /*psession->tcplink=0L;*/ icq_ChatSessionClose(psession); } if(p->type==TCP_LINK_FILE) { icq_FileSession *psession=p->session; psession->tcplink=0L; icq_FileSessionClose(psession); } } /* close the socket after we notify app so app can read errno if necessary */ if (p->socket > -1) { #ifdef _WIN32 closesocket(p->socket); #else close(p->socket); #endif } free(p); } void icq_TCPLinkClose(icq_TCPLink *plink) { list_remove(plink->icqlink->d->icq_TCPLinks, plink); icq_TCPLinkDelete(plink); } int icq_TCPLinkProxyConnect(icq_TCPLink *plink, DWORD uin, int port) { struct sockaddr_in prsin; struct hostent *host_struct; int conct; (void)uin; (void)port; prsin.sin_addr.s_addr = htonl(plink->icqlink->icq_ProxyIP); if(prsin.sin_addr.s_addr == (unsigned long)-1) { prsin.sin_addr.s_addr = inet_addr(plink->icqlink->icq_ProxyHost); if(prsin.sin_addr.s_addr == (unsigned long)-1) /* name isn't n.n.n.n so must be DNS */ { host_struct = gethostbyname(plink->icqlink->icq_ProxyHost); if(host_struct == 0L) { icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Can't find hostname: %s\n", plink->icqlink->icq_ProxyHost); return -1; } prsin.sin_addr = *((struct in_addr *)host_struct->h_addr); } } prsin.sin_family = AF_INET; /* we're using the inet not appletalk*/ prsin.sin_port = htons(plink->icqlink->icq_ProxyPort); /* port */ /* flags = fcntl(plink->socket, F_GETFL, 0); */ /* fcntl(plink->socket, F_SETFL, flags & (~O_NONBLOCK)); */ plink->mode |= TCP_LINK_SOCKS_CONNECTING; conct = connect(plink->socket, (struct sockaddr *) &prsin, sizeof(prsin)); if(conct == -1) /* did we connect ?*/ { if(errno != EINPROGRESS) { conct = errno; icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Connection refused\n"); return conct; } return 1; } return 0; } int icq_TCPLinkProxyRequestAuthorization(icq_TCPLink *plink) { char buf[1024]; plink->mode = (plink->mode & (~TCP_LINK_SOCKS_CONNECTING)); buf[0] = 5; /* protocol version */ buf[1] = 1; /* number of methods */ if(!strlen(plink->icqlink->icq_ProxyName) || !strlen(plink->icqlink->icq_ProxyPass) || !plink->icqlink->icq_ProxyAuth) { buf[2] = 0; /* no authorization required */ plink->mode |= TCP_LINK_SOCKS_NOAUTHSTATUS; } else { buf[2] = 2; /* method username/password */ plink->mode |= TCP_LINK_SOCKS_AUTHORIZATION; } #ifdef _WIN32 if(send(plink->socket, buf, 3, 0) != 3) return errno; #else if(write(plink->socket, buf, 3) != 3) return errno; #endif return 0; } int icq_TCPLinkProxyAuthorization(icq_TCPLink *plink) { int res; char buf[1024]; plink->mode = (plink->mode & (~TCP_LINK_SOCKS_AUTHORIZATION)) | TCP_LINK_SOCKS_AUTHSTATUS; #ifdef _WIN32 res = recv(plink->socket, buf, 2, 0); #else res = read(plink->socket, buf, 2); #endif if(res != 2 || buf[0] != 5 || buf[1] != 2) /* username/password authentication*/ { icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Authentication method incorrect\n"); #ifdef _WIN32 closesocket(plink->socket); #else close(plink->socket); #endif return -1; } buf[0] = 1; /* version of subnegotiation */ buf[1] = strlen(plink->icqlink->icq_ProxyName); memcpy(&buf[2], plink->icqlink->icq_ProxyName, buf[1]); buf[2+buf[1]] = strlen(plink->icqlink->icq_ProxyPass); memcpy(&buf[3+buf[1]], plink->icqlink->icq_ProxyPass, buf[2+buf[1]]); #ifdef _WIN32 if(send(plink->socket, buf, buf[1]+buf[2+buf[1]]+3, 0) != buf[1] + buf[2+buf[1]]+3) return errno; #else if(write(plink->socket, buf, buf[1]+buf[2+buf[1]]+3) != buf[1] + buf[2+buf[1]]+3) return errno; #endif return 0; } int icq_TCPLinkProxyAuthStatus(icq_TCPLink *plink) { int res; char buf[20]; plink->mode = (plink->mode & (~TCP_LINK_SOCKS_AUTHSTATUS)) | TCP_LINK_SOCKS_CROSSCONNECT; #ifdef _WIN32 res = recv(plink->socket, buf, 2, 0); #else res = read(plink->socket, buf, 2); #endif if(res != 2 || buf[0] != 1 || buf[1] != 0) { icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Authorization failure\n"); #ifdef _WIN32 closesocket(plink->socket); #else close(plink->socket); #endif return -1; } return 0; } int icq_TCPLinkProxyNoAuthStatus(icq_TCPLink *plink) { int res; char buf[20]; plink->mode = (plink->mode & (~TCP_LINK_SOCKS_NOAUTHSTATUS)) | TCP_LINK_SOCKS_CROSSCONNECT; #ifdef _WIN32 res = recv(plink->socket, buf, 2, 0); #else res = read(plink->socket, buf, 2); #endif if(res != 2 || buf[0] != 5 || buf[1] != 0) /* no authentication required */ { icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Authentication method incorrect\n"); #ifdef _WIN32 closesocket(plink->socket); #else close(plink->socket); #endif return -1; } return 0; } int icq_TCPLinkProxyCrossConnect(icq_TCPLink *plink) { char buf[20]; plink->mode = (plink->mode & ~(TCP_LINK_SOCKS_CROSSCONNECT)) | TCP_LINK_SOCKS_CONNSTATUS; buf[0] = 5; /* protocol version */ buf[1] = 1; /* command connect */ buf[2] = 0; /* reserved */ buf[3] = 1; /* address type IP v4 */ memcpy(&buf[4], &plink->remote_address.sin_addr.s_addr, 4); memcpy(&buf[8], &plink->remote_address.sin_port, 2); #ifdef _WIN32 if(send(plink->socket, buf, 10, 0) != 10) return errno; #else if(write(plink->socket, buf, 10) != 10) return errno; #endif return 0; } int icq_TCPLinkProxyConnectStatus(icq_TCPLink *plink) { int res; char buf[1024]; plink->mode = (plink->mode & (~TCP_LINK_SOCKS_CONNSTATUS)); #ifdef _WIN32 res = recv(plink->socket, buf, 10, 0); #else res = read(plink->socket, buf, 10); #endif if(res != 10 || buf[0] != 5 || buf[1] != 0) { switch(buf[1]) { case 1: icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] General SOCKS server failure\n"); res = EFAULT; break; case 2: icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Connection not allowed by ruleset\n"); res = EACCES; break; case 3: icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Network unreachable\n"); res = ENETUNREACH; break; case 4: icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Host unreachable\n"); res = ENETUNREACH; break; case 5: icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Connection refused\n"); res = ECONNREFUSED; break; case 6: icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] TTL expired\n"); res = ETIMEDOUT; break; case 7: icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Command not supported\n"); res = EOPNOTSUPP; break; case 8: icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Address type not supported\n"); res = EAFNOSUPPORT; break; default: icq_FmtLog(plink->icqlink, ICQ_LOG_FATAL, "[SOCKS] Unknown SOCKS server failure\n"); res = EFAULT; break; } #ifdef _WIN32 closesocket(plink->socket); #else close(plink->socket); #endif return res; } return 0; } int icq_TCPLinkConnect(icq_TCPLink *plink, DWORD uin, int port) { icq_ContactItem *pcontact=icq_ContactFind(plink->icqlink, uin); icq_Packet *p; int result; #ifndef _WIN32 int flags; #else u_long iosflag; #endif /* these return values never and nowhere checked */ /* denis. */ if(!pcontact) return -2; if((plink->socket=socket(AF_INET, SOCK_STREAM, 0)) < 0) return -3; /* bzero(&(plink->remote_address), sizeof(plink->remote_address)); Win32 incompatible... */ memset(&(plink->remote_address), 0, sizeof(plink->remote_address)); plink->remote_address.sin_family = AF_INET; /* if our IP is the same as the remote user's ip, connect to real_ip instead since we're both probably behind a firewall */ icq_FmtLog(plink->icqlink, ICQ_LOG_MESSAGE, "local IP is %08X:%d, remote real IP is %08X:%d, remote IP is %08X:%d, port is %d\n", plink->icqlink->icq_OurIP, plink->icqlink->icq_OurPort, pcontact->remote_real_ip, pcontact->remote_port, pcontact->remote_ip, pcontact->remote_port, port ); if (plink->icqlink->icq_OurIP == pcontact->remote_ip) plink->remote_address.sin_addr.s_addr = htonl(pcontact->remote_real_ip); else plink->remote_address.sin_addr.s_addr = htonl(pcontact->remote_ip); if(plink->type==TCP_LINK_MESSAGE) { plink->remote_address.sin_port = htons(pcontact->remote_port); icq_FmtLog(plink->icqlink, ICQ_LOG_MESSAGE, "initiating message connect to %d (%s:%d)\n", uin, inet_ntoa(*((struct in_addr *)(&(plink->remote_address.sin_addr)))), pcontact->remote_port); } else { plink->remote_address.sin_port = htons(port); icq_FmtLog(plink->icqlink, ICQ_LOG_MESSAGE, "initiating file/chat connect to %d (%s:%d)\n", uin, inet_ntoa(*((struct in_addr *)(&(plink->remote_address.sin_addr)))), port); } /* set the socket to non-blocking */ #ifdef _WIN32 iosflag = TRUE; ioctlsocket(plink->socket, FIONBIO, &iosflag); #else flags=fcntl(plink->socket, F_GETFL, 0); fcntl(plink->socket, F_SETFL, flags | O_NONBLOCK); #endif if(!plink->icqlink->icq_UseProxy) result=connect(plink->socket, (struct sockaddr *)&(plink->remote_address), sizeof(plink->remote_address)); else /* SOCKS proxy support */ result=icq_TCPLinkProxyConnect(plink, uin, port); /* FIXME: Here we should check for errors on connection */ /* because of proxy support - it can't be checked */ /* by getsockopt() later in _handle_ready_sockets() */ /* denis. */ plink->mode|=TCP_LINK_MODE_CONNECTING; plink->remote_uin=uin; plink->connect_time=time(0L); /* Send the hello packet */ p=icq_TCPCreateInitPacket(plink); icq_TCPLinkSend(plink, p); #ifdef TCP_PACKET_TRACE printf("hello packet queued for %lu\n", uin); #endif /* TCP_PACKET_TRACE */ return 1; } icq_TCPLink *icq_TCPLinkAccept(icq_TCPLink *plink) { #ifdef _WIN32 u_long iosflag; #else int flags; #endif int socket; size_t remote_length; icq_TCPLink *pnewlink=icq_TCPLinkNew( plink->icqlink ); if(pnewlink) { socket=accept(plink->socket, (struct sockaddr *)&(plink->remote_address), &remote_length); icq_FmtLog(plink->icqlink, ICQ_LOG_MESSAGE, "accepting tcp connection from %s:%d\n", inet_ntoa(*((struct in_addr *)(&(plink->remote_address.sin_addr)))), ntohs(plink->remote_address.sin_port)); /* FIXME: make sure accept succeeded */ pnewlink->type=plink->type; pnewlink->socket=socket; /* first packet sent on an icq tcp link is always the hello packet */ pnewlink->mode|=TCP_LINK_MODE_HELLOWAIT; } /* set the socket to non-blocking */ #ifdef _WIN32 iosflag = TRUE; ioctlsocket(plink->socket, FIONBIO, &iosflag); #else flags=fcntl(pnewlink->socket, F_GETFL, 0); fcntl(pnewlink->socket, F_SETFL, flags | O_NONBLOCK); #endif return pnewlink; } int icq_TCPLinkListen(icq_TCPLink *plink) { unsigned int t; /* listening links have 0 uin */ plink->remote_uin=0; /* create tcp listen socket */ if((plink->socket=socket(AF_INET, SOCK_STREAM, 0)) < 0) return -1; /* must use memset, no bzero for Win32! */ memset(&plink->socket_address, 0, sizeof(struct sockaddr_in)); plink->socket_address.sin_family=AF_INET; plink->socket_address.sin_addr.s_addr=htonl(INADDR_ANY); plink->socket_address.sin_port=0; if(bind(plink->socket, (struct sockaddr *)&plink->socket_address, sizeof(struct sockaddr_in)) < 0) return -2; if(listen(plink->socket, 5) < 0) return -3; t=sizeof(struct sockaddr_in); if(getsockname(plink->socket, (struct sockaddr *)&plink->socket_address, &t) < 0) return -4; icq_FmtLog(plink->icqlink, ICQ_LOG_MESSAGE, "created tcp listening socket %d, local address=%s:%d\n", plink->socket, inet_ntoa(*((struct in_addr *)(&plink->socket_address.sin_addr))), ntohs(plink->socket_address.sin_port)); plink->mode|=TCP_LINK_MODE_LISTEN; return 0; } /* Doing Cyrillic translations for Chat dialog sessions */ void icq_ChatRusConv_n(const char to[4], char *t_in, int t_len) { int i, j; for(i = j = 0; i < t_len; ++i) { if((((unsigned char)t_in[i]) < ' ') && (t_in[i] != '\r')) { if(i - 1 > j) icq_RusConv_n(to, &t_in[j], i - j - 1); switch(t_in[i]) { case '\x07': /* Bell */ case '\x08': /* BackSpace */ case '\x03': /* Chat is active */ case '\x04': /* Chat is not active */ break; case '\x00': /* Foregroung color (RR GG BB ?? ) */ case '\x01': /* Background color (RR GG BB ?? ) */ case '\x11': /* Font style change (Bold - 1, Italic - 2, Underline - 4) */ case '\x12': /* Font size change */ i += 4; break; case '\x10': /* Font family and encoding change */ i += t_in[i+1] + 2 + 2; icq_RusConv_n(to, &t_in[i+3], t_in[i+1]); break; } j = i + 1; } } if(i > t_len) i = t_len; if(j > t_len) j = t_len; if(i > j) icq_RusConv_n(to, &t_in[j], i - j); } int icq_TCPLinkOnDataReceived(icq_TCPLink *plink) { int process_count=0, recv_result=0; char *buffer=plink->buffer; do { /* while recv_result > 0 */ int done=0; /* append received data onto end of buffer */ if((recv_result=recv(plink->socket, buffer+plink->buffer_count, icq_TCPLinkBufferSize-plink->buffer_count, 0)) < 1) { /* either there was an error or the remote side has closed * the connection - fall out of the loop */ continue; }; plink->buffer_count+=recv_result; #ifdef TCP_BUFFER_TRACE printf("received %d bytes from link %x, new buffer count %d\n", recv_result, plink, plink->buffer_count); hex_dump(plink->buffer, plink->buffer_count); #endif /*TCP_BUFFER_TRACE*/ process_count+=recv_result; /* don't do any packet processing if we're in raw mode */ if(plink->mode & TCP_LINK_MODE_RAW) { /* notify the app with the new data */ if(plink->type == TCP_LINK_CHAT) icq_ChatRusConv_n("wk", plink->buffer, plink->buffer_count); if(plink->icqlink->icq_RequestNotify) (*plink->icqlink->icq_RequestNotify)(plink->icqlink, plink->id, ICQ_NOTIFY_CHATDATA, plink->buffer_count, plink->buffer); plink->buffer_count=0; continue; } /* remove packets from the buffer until the buffer is empty * or the remaining bytes do not equal a full packet */ while((unsigned)plink->buffer_count>sizeof(WORD) && !done) { WORD packet_size=(*((WORD *)buffer)); /* warn if the buffer is too small to hold the whole packet */ if(packet_size>icq_TCPLinkBufferSize-sizeof(WORD)) { icq_FmtLog(plink->icqlink, ICQ_LOG_WARNING, "tcplink buffer " "overflow, packet size = %d, buffer size = %d, closing link\n", packet_size, icq_TCPLinkBufferSize); return 0; } if(packet_size+sizeof(WORD) <= (unsigned)plink->buffer_count) { /* copy the packet into memory */ icq_Packet *p=icq_PacketNew(); icq_PacketAppend(p, buffer+sizeof(WORD), packet_size); /* remove it from the buffer */ memcpy(buffer, buffer+packet_size+sizeof(WORD), plink->buffer_count-packet_size-sizeof(WORD)); plink->buffer_count-=(packet_size+sizeof(WORD)); icq_TCPLinkOnPacketReceived(plink, p); } else { /* not enough bytes in buffer to form the complete packet. * we're done for now */ done=1; } } /* while packets remain in buffer */ } while (recv_result > 0); if (recv_result < 0 && errno!=EWOULDBLOCK) { /* receive error - log it */ icq_FmtLog(plink->icqlink, ICQ_LOG_WARNING, "recv failed from %d (%d-%s)," " closing link\n", plink->remote_uin, errno, strerror(errno)); } return process_count; } void icq_TCPLinkOnPacketReceived(icq_TCPLink *plink, icq_Packet *p) { #ifdef TCP_RAW_TRACE printf("packet received! { length=%d }\n", p->length); icq_PacketDump(p); #endif /* Stick packet on ready packet linked list */ list_enqueue(plink->received_queue, p); } void icq_TCPLinkOnConnect(icq_TCPLink *plink) { #ifdef _WIN32 int len; #else size_t len; #endif int error; /* check getsockopt */ len=sizeof(error); #ifdef _WIN32 getsockopt(plink->socket, SOL_SOCKET, SO_ERROR, (char *)&error, &len); #else getsockopt(plink->socket, SOL_SOCKET, SO_ERROR, &error, &len); #endif if(!error && (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))) { if(plink->mode & TCP_LINK_SOCKS_CONNECTING) error = icq_TCPLinkProxyRequestAuthorization(plink); else if(plink->mode & TCP_LINK_SOCKS_AUTHORIZATION) error = icq_TCPLinkProxyAuthorization(plink); else if(plink->mode & TCP_LINK_SOCKS_AUTHSTATUS) error = icq_TCPLinkProxyAuthStatus(plink); else if(plink->mode & TCP_LINK_SOCKS_NOAUTHSTATUS) error = icq_TCPLinkProxyNoAuthStatus(plink); else if(plink->mode & TCP_LINK_SOCKS_CROSSCONNECT) error = icq_TCPLinkProxyCrossConnect(plink); else if(plink->mode & TCP_LINK_SOCKS_CONNSTATUS) error = icq_TCPLinkProxyConnectStatus(plink); else error = EINVAL; } if(error) { /* connection failed- close the link, which takes care * of notifying the app about packets that didn't make it */ icq_FmtLog(plink->icqlink, ICQ_LOG_WARNING, "connect failed to %d (%d-%s)," " closing link\n", plink->remote_uin, error, strerror(error)); icq_TCPLinkClose(plink); return; } 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)) return; len=sizeof(plink->socket_address); getsockname(plink->socket, (struct sockaddr *)&plink->socket_address, &len); icq_FmtLog(plink->icqlink, ICQ_LOG_MESSAGE, "connected to uin %d, socket=%d local address=%s:%d remote address=%s:%d\n", plink->remote_uin, plink->socket, inet_ntoa(*((struct in_addr *)(&plink->socket_address.sin_addr))), ntohs(plink->socket_address.sin_port), inet_ntoa(*((struct in_addr *)(&plink->remote_address.sin_addr))), ntohs(plink->remote_address.sin_port)); plink->mode&= ~TCP_LINK_MODE_CONNECTING; /* socket is now connected, notify each request that connection * has been established and send pending data */ while(plink->send_queue->count>0) { icq_Packet *p=list_dequeue(plink->send_queue); if(p->id) if(plink->icqlink->icq_RequestNotify) (*plink->icqlink->icq_RequestNotify)(plink->icqlink, p->id, ICQ_NOTIFY_CONNECTED, 0, 0); icq_TCPLinkSend(plink, p); } /* yeah this probably shouldn't be here. oh well :) */ if(plink->type==TCP_LINK_CHAT) { icq_ChatSessionSetStatus((icq_ChatSession *)plink->session, CHAT_STATUS_CONNECTED); icq_ChatSessionSetStatus((icq_ChatSession *)plink->session, CHAT_STATUS_WAIT_ALLINFO); } if(plink->type==TCP_LINK_FILE) { icq_FileSessionSetStatus((icq_FileSession *)plink->session, FILE_STATUS_CONNECTED); } } unsigned long icq_TCPLinkSendSeq(icq_TCPLink *plink, icq_Packet *p, unsigned long sequence) { /* append the next sequence number on the packet */ if (!sequence) sequence=plink->icqlink->d->icq_TCPSequence--; p->id=sequence; icq_PacketEnd(p); icq_PacketAppend32(p, sequence); /* if the link is currently connecting, queue the packets for * later, else send immediately */ if(plink->mode & TCP_LINK_MODE_CONNECTING) { list_insert(plink->send_queue, 0, p); if(plink->icqlink->icq_RequestNotify) (*plink->icqlink->icq_RequestNotify)(plink->icqlink, p->id, ICQ_NOTIFY_CONNECTING, 0, 0); } else { icq_PacketSend(p, plink->socket); if(p->id) if(plink->icqlink->icq_RequestNotify) (*plink->icqlink->icq_RequestNotify)(plink->icqlink, p->id, ICQ_NOTIFY_SENT, 0, 0); icq_PacketDelete(p); } return sequence; } void icq_TCPLinkSend(icq_TCPLink *plink, icq_Packet *p) { /* if the link is currently connecting, queue the packets for * later, else send immediately */ if(plink->mode & TCP_LINK_MODE_CONNECTING) { list_insert(plink->send_queue, 0, p); if(plink->icqlink->icq_RequestNotify) (*plink->icqlink->icq_RequestNotify)(plink->icqlink, p->id, ICQ_NOTIFY_CONNECTING, 0, 0); } else { icq_PacketSend(p, plink->socket); if(p->id) if(plink->icqlink->icq_RequestNotify) (*plink->icqlink->icq_RequestNotify)(plink->icqlink, p->id, ICQ_NOTIFY_SENT, 0, 0); icq_PacketDelete(p); } } void icq_TCPLinkProcessReceived(icq_TCPLink *plink) { list *plist=plink->received_queue; while(plist->count>0) { icq_Packet *p=list_dequeue(plist); if(plink->mode & TCP_LINK_MODE_HELLOWAIT) { icq_TCPProcessHello(p, plink); } else { switch (plink->type) { case TCP_LINK_MESSAGE: icq_TCPProcessPacket(p, plink); break; case TCP_LINK_CHAT: icq_TCPProcessChatPacket(p, plink); break; case TCP_LINK_FILE: icq_TCPProcessFilePacket(p, plink); break; } } icq_PacketDelete(p); } } int _icq_FindTCPLink(void *p, va_list data) { icq_TCPLink *plink=(icq_TCPLink *)p; unsigned long uin=va_arg(data, unsigned long); int type=va_arg(data, int); return ( (plink->remote_uin == uin ) && (plink->type == type) ); } icq_TCPLink *icq_FindTCPLink(ICQLINK *link, unsigned long uin, int type) { return list_traverse(link->d->icq_TCPLinks, _icq_FindTCPLink, uin, type); }