From ff5a775b94465b30897964630af600fe5915fc51 Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy Date: Tue, 2 Apr 2013 13:54:21 +0000 Subject: VypressChat added (not adopted) git-svn-id: http://svn.miranda-ng.org/main/trunk@4285 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/!NotAdopted/VypressChat/libvqproto/link.c | 611 ++++++++++++++++++++++ 1 file changed, 611 insertions(+) create mode 100644 plugins/!NotAdopted/VypressChat/libvqproto/link.c (limited to 'plugins/!NotAdopted/VypressChat/libvqproto/link.c') diff --git a/plugins/!NotAdopted/VypressChat/libvqproto/link.c b/plugins/!NotAdopted/VypressChat/libvqproto/link.c new file mode 100644 index 0000000000..876af36d03 --- /dev/null +++ b/plugins/!NotAdopted/VypressChat/libvqproto/link.c @@ -0,0 +1,611 @@ +/* + * libvqproto: Vypress/QChat protocol interface library + * (c) Saulius Menkevicius 2005 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: link.c,v 1.14 2005/03/08 17:21:23 bobas Exp $ + */ + +#include +#include + +#include "vqproto.h" +#include "link.h" +#include "message.h" + +/* global data + */ +#ifndef MEMWATCH +void * (* vqp_mmi_malloc)(size_t) = NULL; +void (* vqp_mmi_free)(void *) = NULL; +#endif + +/* static routines + */ +static __inline unsigned short +ushort2bcd(unsigned short ushort) +{ + return (ushort % 10) | (((ushort / 10) % 10) << 4) + | (((ushort / 100) % 10) << 8) | (((ushort / 1000) % 10) << 12); +} + +static void +vqp_make_msg_sig(struct vqp_message_struct * msg) +{ + static unsigned rand_num = 0; + int i; + + /* hash message contents to get some initial random num */ + if(!rand_num) { + for(i=0; i < msg->content_len; i++) + rand_num += msg->content[i]; + rand_num %= 211; + } + + /* make sure the rng is seeded correctly */ + srand((unsigned) time(NULL) + rand_num++); + + /* generate packet signature */ + for(i=0; i < VQP_LINK_SIG_LEN; i++) + msg->sig[i] = (unsigned char)('0' + rand() % ('9' - '0' + 1)); + + /* add '\0' at the end (not truly necessary, but it helps to have + * asciiz string instead of an unbounded char[] + */ + msg->sig[VQP_LINK_SIG_LEN] = '\0'; +} + +static int +vqp_is_seen_msg_sig(struct vqp_link_struct * link, char * sig) +{ + int i; + for(i = 0; i < VQP_LINK_SEEN_SIGS_NUM; i++) + if(!memcmp(link->seen_sigs[i], sig, VQP_LINK_SIG_LEN)) + return 1; + return 0; +} + +static void +vqp_add_seen_msg_sig(struct vqp_message_struct * msg) +{ + memcpy(msg->link->seen_sigs[msg->link->seen_sig_inc], + msg->sig, VQP_LINK_SIG_LEN); + msg->link->seen_sig_inc = (msg->link->seen_sig_inc + 1) + % VQP_LINK_SEEN_SIGS_NUM; +} + +static int +vqp_link_open_setup_udp_multicast(struct vqp_link_struct * link) +{ + struct ip_mreq mreq; + const unsigned char opt_loop = 1; + const unsigned char ttl = 32; + + /* set IP_MULTICAST_LOOP to 1, so our host receives + * the messages we send + */ + if(setsockopt( link->tx_socket, IPPROTO_IP, IP_MULTICAST_LOOP, + (void*)&opt_loop, sizeof(opt_loop) + ) != 0) + return -1; + + /* set IP_MULTICAST_TTL to 32, that is the packets + * will go through 32 routers before getting scrapped + */ + if(setsockopt( link->tx_socket, IPPROTO_IP, IP_MULTICAST_TTL, + (void *)&ttl, sizeof(ttl) + ) != 0) + return -1; + + + /* set our group membership */ + mreq.imr_multiaddr.s_addr = htonl(link->conndata.udp.multicast_address); + mreq.imr_interface.s_addr = INADDR_ANY; + if(setsockopt( link->rx_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (void *)&mreq, sizeof(mreq) + ) != 0) + return -1; + + return 0; +} + +static int +vqp_link_open_setup_tx_broadcast(struct vqp_link_struct * link) +{ + const int sock_opt = 1; + return setsockopt( + link->tx_socket, SOL_SOCKET, SO_BROADCAST, + (void *)&sock_opt, sizeof(sock_opt)); +} + +static int +vqp_link_open_setup_udp(struct vqp_link_struct * link, int * p_error) +{ + int setup_result; + const int sockopt_true = 1; + struct sockaddr_in sin; + + /* setup tx socket */ + link->tx_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if(link->tx_socket < 0) { + if(p_error) + *p_error = errno; + return 1; + } + + link->rx_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if(link->rx_socket < 0) { + if(p_error) + *p_error = errno; + + closesocket(link->tx_socket); + return 1; + } + + /* on win32 we can setup the socket so we can + * use multiple clients on the same pc + */ +#ifdef _WIN32 + setsockopt( + link->rx_socket, SOL_SOCKET, SO_REUSEADDR, + (void *)&sockopt_true, sizeof(sockopt_true)); +#endif + + /* bind rx socket */ + sin.sin_family = PF_INET; + sin.sin_addr.s_addr = htonl(link->conndata.udp.local_address + ? link->conndata.udp.local_address + : INADDR_ANY); + sin.sin_port = htons(link->port); + if(bind(link->rx_socket, (struct sockaddr *)&sin, sizeof(sin)) < 0) { + if(p_error) + *p_error = errno; + + closesocket(link->rx_socket); + closesocket(link->tx_socket); + return 1; + } + + link->tx_socket = link->rx_socket; + + /* setup sockets for multicast or broadcast service + */ + setup_result = (link->options & VQP_PROTOCOL_OPT_MULTICAST) + ? vqp_link_open_setup_udp_multicast(link) + : vqp_link_open_setup_tx_broadcast(link); + + if(setup_result < 0) { + if(p_error) + *p_error = errno; + + closesocket(link->rx_socket); + closesocket(link->tx_socket); + return 1; + } + + /* success */ + return 0; +} + +static int vqp_link_open_setup_ipx(struct vqp_link_struct * link, int * p_error) +{ + struct sockaddr_ipx six; + + /* setup tx socket */ + link->tx_socket = socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX); + if(link->tx_socket < 0) { + if(p_error) + *p_error = errno; + return 1; + } + + if(vqp_link_open_setup_tx_broadcast(link)) { + if(p_error) + *p_error = errno; + + closesocket(link->tx_socket); + return 1; + } + + /* setup rx socket */ + link->rx_socket = socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX); + if(link->rx_socket < 0) { + if(p_error) + *p_error = errno; + closesocket(link->rx_socket); + return 1; + } + + /* bind rx socket */ + memset(&six, 0, sizeof(six)); + six.sa_family = AF_IPX; + six.sa_socket = htons(ushort2bcd(link->port)); + + if(bind(link->rx_socket, (struct sockaddr *)&six, sizeof(six))) { + if(p_error) + *p_error = errno; + + closesocket(link->rx_socket); + closesocket(link->tx_socket); + return 1; + } + + /* save node and network number */ + memcpy(link->conndata.ipx.netnum, six.sa_netnum, 4); + memcpy(link->conndata.ipx.nodenum, six.sa_nodenum, 6); + + /* success */ + return 0; +} + +/* exported routines + */ +void vqp_init( + void * (* mmi_malloc_func)(size_t), + void (* mmi_free_func)(void *)) +{ +#ifndef MEMWATCH + if(mmi_malloc_func) + vqp_mmi_malloc = mmi_malloc_func; + else vqp_mmi_malloc = malloc; + + if(mmi_free_func) + vqp_mmi_free = mmi_free_func; + else vqp_mmi_free = free; +#endif +} + +void vqp_uninit() +{ +} + +vqp_link_t vqp_link_open( + enum vqp_protocol_type protocol, + enum vqp_protocol_options options, + enum vqp_protocol_connection connection, + unsigned long local_address, + unsigned long * p_broadcast_addresses, /* 0UL terminated list */ + unsigned long multicast_address, + unsigned short port, + int * p_error) +{ + struct vqp_link_struct * link; + + /* alloc and init link struct */ + link = vqp_mmi_malloc(sizeof(struct vqp_link_struct)); + if(!link) { + if(p_error) *p_error = ENOMEM; + return NULL; + } + + link->rx_socket = 0; + link->tx_socket = 0; + link->protocol = protocol; + link->options = options; + link->connection = connection; + link->port = port ? port: 8167; + link->seen_sig_inc = 0; + memset(link->seen_sigs, 0, VQP_LINK_SEEN_SIGS_NUM * VQP_LINK_SIG_LEN); + + if(connection == VQP_PROTOCOL_CONN_UDP) { + /* UDP + */ + link->conndata.udp.local_address = local_address; + link->conndata.udp.multicast_address = multicast_address; + link->conndata.udp.p_broadcast_addresses = NULL; + + if(vqp_link_open_setup_udp(link, p_error)) { + vqp_mmi_free(link); + return NULL; + } + + /* setup broadcast masks lists + */ + if(!(link->options & VQP_PROTOCOL_OPT_MULTICAST)) { + /* standard broadcast + */ + int i; + for(i = 0; p_broadcast_addresses[i]; i++) /* nothing */ ; + + if(!i) { + /* no broadcast addresses defined: + * use 255.255.255.255 */ + link->conndata.udp.p_broadcast_addresses + = vqp_mmi_malloc(sizeof(unsigned long) * 2); + + if(!link->conndata.udp.p_broadcast_addresses) { + vqp_mmi_free(link); + if(p_error) + *p_error = ENOMEM; + + closesocket(link->rx_socket); + closesocket(link->tx_socket); + return NULL; + } + + link->conndata.udp.p_broadcast_addresses[0] = 0xffffffffUL; + link->conndata.udp.p_broadcast_addresses[1] = 0UL; + } else { + /* copy broadcast addresses */ + size_t listsz = sizeof(unsigned long) * (i + 1); + link->conndata.udp.p_broadcast_addresses = vqp_mmi_malloc(listsz); + + if(link->conndata.udp.p_broadcast_addresses == NULL) { + vqp_mmi_free(link); + if(p_error) + *p_error = ENOMEM; + + closesocket(link->rx_socket); + closesocket(link->tx_socket); + return NULL; + } + memcpy(link->conndata.udp.p_broadcast_addresses, + p_broadcast_addresses, listsz); + } + } + } + else { + /* IPX + */ + if(vqp_link_open_setup_ipx(link, p_error)) { + vqp_mmi_free(link); + return NULL; + } + } + + return (vqp_link_t)link; +} + +int vqp_link_close(vqp_link_t vqlink) +{ + struct vqp_link_struct * link = P_VQP_LINK_STRUCT(vqlink); + + closesocket(link->tx_socket); + closesocket(link->rx_socket); + + if(link->connection == VQP_PROTOCOL_CONN_UDP && link->conndata.udp.p_broadcast_addresses) + vqp_mmi_free(link->conndata.udp.p_broadcast_addresses); + + vqp_mmi_free(link); + + return 0; +} + +int vqp_link_rx_socket(vqp_link_t link) +{ + return P_VQP_LINK_STRUCT(link)->rx_socket; +} + +enum vqp_protocol_type +vqp_link_protocol(vqp_link_t link) +{ + return P_VQP_LINK_STRUCT(link)->protocol; +} + +int vqp_link_send(vqp_msg_t vqmsg) +{ + struct vqp_message_struct * msg = P_VQP_MESSAGE_STRUCT(vqmsg); + char * packet; + size_t packet_len; + + /* check that the message contains something */ + if(msg->content_len == 0) + return EINVAL; + + /* check that we have the correct msg dst addr (if specified) + */ + + if(msg->link->protocol == VQP_PROTOCOL_VYPRESSCHAT) { + /* assign & register unique packet id for the message */ + vqp_make_msg_sig(msg); + vqp_add_seen_msg_sig(msg); + + /* alloc real packet contents with signature */ + packet_len = 1 + VQP_LINK_SIG_LEN + msg->content_len; + packet = vqp_mmi_malloc(packet_len); + if(!packet) + return ENOMEM; + + /* fill packet contents in */ + packet[0] = 'X'; /* vypress chat packet */ + memcpy(packet + 1, msg->sig, VQP_LINK_SIG_LEN); + memcpy(packet + 1 + VQP_LINK_SIG_LEN, + msg->content, msg->content_len); + } else { + /* there's no packet sig to add for quickchat packets */ + packet = msg->content; + packet_len = msg->content_len; + } + + if(msg->link->connection == VQP_PROTOCOL_CONN_UDP) { + /* IP/UDP transport + */ + struct sockaddr_in sin; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = PF_INET; + sin.sin_port = htons(msg->link->port); + + /* send message to all the netmasks/multicasts specified */ + if(!vqp_addr_is_nil(&msg->dst_addr)) { + /* send packet directly to specified address + */ + sin.sin_addr.s_addr = htonl(msg->dst_addr.node.ip); + sendto(msg->link->tx_socket, packet, packet_len, 0, + (struct sockaddr *)&sin, sizeof(sin)); + } + else if(msg->link->protocol == VQP_PROTOCOL_VYPRESSCHAT + && (msg->link->options & VQP_PROTOCOL_OPT_MULTICAST)) + { + /* send packet to multicast group + */ + sin.sin_addr.s_addr = htonl(msg->link->conndata.udp.multicast_address); + sendto(msg->link->tx_socket, packet, packet_len, 0, + (struct sockaddr *)&sin, sizeof(sin)); + } + else { + /* send packet to multiple broadcast addresses + */ + int n; + for(n = 0; msg->link->conndata.udp.p_broadcast_addresses[n] != 0; n++) { + sin.sin_addr.s_addr = htonl( + msg->link->conndata.udp.p_broadcast_addresses[n]); + + sendto(msg->link->tx_socket, packet, packet_len, 0, + (struct sockaddr *)&sin, sizeof(sin)); + } + } + } + else if(msg->link->connection == VQP_PROTOCOL_CONN_IPX) { + /* IPX transport + */ + struct sockaddr_ipx six; + + memset(&six, 0, sizeof(six)); + six.sa_family = AF_IPX; + six.sa_socket = htons(ushort2bcd(msg->link->port)); + + if(!vqp_addr_is_nil(&msg->dst_addr)) { + /* send packet to specified address + */ + memcpy(six.sa_netnum, msg->link->conndata.ipx.netnum, 4); + memcpy(six.sa_nodenum, msg->dst_addr.node.ipx, 6); + } + else { + /* send packet to broadcast + */ + memset(six.sa_netnum, 0, 4); + memset(six.sa_nodenum, 0xff, 6); + } + + sendto( msg->link->tx_socket, packet, packet_len, 0, + (struct sockaddr *)&six, sizeof(six)); + } + + /* free packet data */ + if(packet != msg->content) + vqp_mmi_free(packet); + + return 0; /* packet sent ok */ +} + +int vqp_link_recv(vqp_link_t vqlink, vqp_msg_t * p_in_msg) +{ + struct vqp_link_struct * link = P_VQP_LINK_STRUCT(vqlink); + struct vqp_message_struct * msg; + struct sockaddr_in sin; + struct sockaddr_ipx six; + socklen_t sa_len; + char * buf; + ssize_t buf_data_len; + + /* receive the msg */ + buf = vqp_mmi_malloc(VQP_MAX_PACKET_SIZE); + + if(link->connection == VQP_PROTOCOL_CONN_UDP) { + sa_len = sizeof(sin); + buf_data_len = recvfrom( + link->rx_socket, (void*)buf, VQP_MAX_PACKET_SIZE, 0, + (struct sockaddr *) &sin, &sa_len); + } else { + sa_len = sizeof(six); + buf_data_len = recvfrom( + link->rx_socket, (void *)buf, VQP_MAX_PACKET_SIZE, 0, + (struct sockaddr *) &six, &sa_len); + } + + if(buf_data_len <= 1) { + vqp_mmi_free(buf); + return errno; + } + + if(link->protocol == VQP_PROTOCOL_VYPRESSCHAT) { + /* check that the packets begins with 'X' and contains + * a signature */ + if(buf[0] != 'X' || buf_data_len < (1 + VQP_LINK_SIG_LEN + 1)) { + vqp_mmi_free(buf); + return 1; + } + + /* check that the signature is not already seen */ + if(vqp_is_seen_msg_sig(link, buf + 1)) { + vqp_mmi_free(buf); + return 1; + } + } + + /* alloc message */ + msg = vqp_mmi_malloc(sizeof(struct vqp_message_struct)); + if(!msg) return 1; + + msg->link = link; + msg->src_addr.conn = link->connection; + if(link->connection == VQP_PROTOCOL_CONN_UDP) { + msg->src_addr.node.ip = ntohl(sin.sin_addr.s_addr); + } else { + memcpy(msg->src_addr.node.ipx, six.sa_nodenum, 6); + } + + /* copy contents */ + msg->content_len = (link->protocol == VQP_PROTOCOL_VYPRESSCHAT) + ? buf_data_len - 1 - VQP_LINK_SIG_LEN + : buf_data_len; + + msg->content = vqp_mmi_malloc(msg->content_len); + if(!msg->content) { + vqp_mmi_free(buf); + vqp_mmi_free(msg); + return 1; + } + + if(link->protocol == VQP_PROTOCOL_VYPRESSCHAT) { + /* copy signature */ + memcpy(msg->sig, buf + 1, VQP_LINK_SIG_LEN); + msg->sig[VQP_LINK_SIG_LEN] = '\0'; + + /* copy contents */ + memcpy(msg->content, buf + 1 + VQP_LINK_SIG_LEN, msg->content_len); + } else { + memcpy(msg->content, buf, msg->content_len); + } + + /* free packet buffer */ + vqp_mmi_free(buf); + + /* return the msg */ + *p_in_msg = msg; + return 0; +} + +void vqp_addr_nil(vqp_link_t link, vqp_addr_t * p_addr) +{ + p_addr->conn = P_VQP_LINK_STRUCT(link)->connection; + memset(&p_addr->node, 0, sizeof(union vqp_addr_node_union)); +} + +int vqp_addr_is_nil(vqp_addr_t * p_addr) +{ + int is_nil; + + if(p_addr->conn == VQP_PROTOCOL_CONN_UDP) { + is_nil = (p_addr->node.ip == 0); + } else { + char nil_ipx[6] = { 0, 0, 0, 0, 0, 0 }; + is_nil = memcmp(nil_ipx, p_addr->node.ipx, 6) == 0; + } + return is_nil; +} + -- cgit v1.2.3