📄 flap_connection.c
字号:
/* * Purple's oscar protocol plugin * This file is the legal property of its developers. * Please see the AUTHORS file distributed alongside this file. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA*/#include "oscar.h"#include "eventloop.h"#include "proxy.h"#ifndef _WIN32#include <netdb.h>#include <sys/socket.h>#include <netinet/in.h>#endif#ifdef _WIN32#include "win32dep.h"#endif/** * This sends a channel 1 SNAC containing the FLAP version. * The FLAP version is sent by itself at the beginning of every * connection to a FLAP server. It is always the very first * packet sent by both the server and the client after the SYN, * SYN/ACK, ACK handshake. */voidflap_connection_send_version(OscarData *od, FlapConnection *conn){ FlapFrame *frame; frame = flap_frame_new(od, 0x01, 4); byte_stream_put32(&frame->data, 0x00000001); flap_connection_send(conn, frame);}/** * This sends a channel 1 FLAP containing the FLAP version and * the authentication cookie. This is sent when connecting to * any FLAP server after the initial connection to the auth * server. It is always the very first packet sent by both the * server and the client after the SYN, SYN/ACK, ACK handshake. */voidflap_connection_send_version_with_cookie(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy){ FlapFrame *frame; GSList *tlvlist = NULL; frame = flap_frame_new(od, 0x01, 4 + 2 + 2 + length); byte_stream_put32(&frame->data, 0x00000001); aim_tlvlist_add_raw(&tlvlist, 0x0006, length, chipsahoy); aim_tlvlist_write(&frame->data, &tlvlist); aim_tlvlist_free(tlvlist); flap_connection_send(conn, frame);}static struct rateclass *flap_connection_get_rateclass(FlapConnection *conn, guint16 family, guint16 subtype){ GSList *tmp1; gconstpointer key; key = GUINT_TO_POINTER((family << 16) + subtype); for (tmp1 = conn->rateclasses; tmp1 != NULL; tmp1 = tmp1->next) { struct rateclass *rateclass; rateclass = tmp1->data; if (g_hash_table_lookup(rateclass->members, key)) return rateclass; } return NULL;}/* * Attempt to calculate what our new current average would be if we * were to send a SNAC in this rateclass at the given time. */static guint32rateclass_get_new_current(FlapConnection *conn, struct rateclass *rateclass, struct timeval *now){ unsigned long timediff; /* In milliseconds */ timediff = (now->tv_sec - rateclass->last.tv_sec) * 1000 + (now->tv_usec - rateclass->last.tv_usec) / 1000; /* This formula is taken from the joscar API docs. Preesh. */ return MIN(((rateclass->current * (rateclass->windowsize - 1)) + timediff) / rateclass->windowsize, rateclass->max);}static gboolean flap_connection_send_queued(gpointer data){ FlapConnection *conn; struct timeval now; conn = data; gettimeofday(&now, NULL); while (!g_queue_is_empty(conn->queued_snacs)) { QueuedSnac *queued_snac; struct rateclass *rateclass; queued_snac = g_queue_peek_head(conn->queued_snacs); rateclass = flap_connection_get_rateclass(conn, queued_snac->family, queued_snac->subtype); if (rateclass != NULL) { guint32 new_current; new_current = rateclass_get_new_current(conn, rateclass, &now); if (new_current < rateclass->alert + 100) /* (Add 100ms padding to account for inaccuracies in the calculation) */ /* Not ready to send this SNAC yet--keep waiting. */ return TRUE; rateclass->current = new_current; rateclass->last.tv_sec = now.tv_sec; rateclass->last.tv_usec = now.tv_usec; } flap_connection_send(conn, queued_snac->frame); g_free(queued_snac); g_queue_pop_head(conn->queued_snacs); } conn->queued_timeout = 0; return FALSE;}/** * This sends a channel 2 FLAP containing a SNAC. The SNAC family and * subtype are looked up in the rate info for this connection, and if * sending this SNAC will induce rate limiting then we delay sending * of the SNAC by putting it into an outgoing holding queue. * * @param data The optional bytestream that makes up the data portion * of this SNAC. For empty SNACs this should be NULL. */voidflap_connection_send_snac(OscarData *od, FlapConnection *conn, guint16 family, guint16 subtype, guint16 flags, aim_snacid_t snacid, ByteStream *data){ FlapFrame *frame; guint32 length; gboolean enqueue = FALSE; struct rateclass *rateclass; length = data != NULL ? data->offset : 0; frame = flap_frame_new(od, 0x02, 10 + length); aim_putsnac(&frame->data, family, subtype, flags, snacid); if (length > 0) { byte_stream_rewind(data); byte_stream_putbs(&frame->data, data, length); } if (conn->queued_timeout != 0) enqueue = TRUE; else if ((rateclass = flap_connection_get_rateclass(conn, family, subtype)) != NULL) { struct timeval now; guint32 new_current; gettimeofday(&now, NULL); new_current = rateclass_get_new_current(conn, rateclass, &now); if (new_current < rateclass->alert + 100) { /* (Add 100ms padding to account for inaccuracies in the calculation) */ enqueue = TRUE; } else { rateclass->current = new_current; rateclass->last.tv_sec = now.tv_sec; rateclass->last.tv_usec = now.tv_usec; } } if (enqueue) { /* We've been sending too fast, so delay this message */ QueuedSnac *queued_snac; queued_snac = g_new(QueuedSnac, 1); queued_snac->family = family; queued_snac->subtype = subtype; queued_snac->frame = frame; g_queue_push_tail(conn->queued_snacs, queued_snac); if (conn->queued_timeout == 0) conn->queued_timeout = purple_timeout_add(500, flap_connection_send_queued, conn); return; } flap_connection_send(conn, frame);}/** * This sends an empty channel 4 FLAP. This is sent to signify * that we're logging off. This shouldn't really be necessary-- * usually the AIM server will detect that the TCP connection has * been destroyed--but it's good practice. */static voidflap_connection_send_close(OscarData *od, FlapConnection *conn){ FlapFrame *frame; frame = flap_frame_new(od, 0x04, 0); flap_connection_send(conn, frame);}/** * This sends an empty channel 5 FLAP. This is used as a keepalive * packet in FLAP connections. WinAIM 4.x and higher send these * _every minute_ to keep the connection alive. */voidflap_connection_send_keepalive(OscarData *od, FlapConnection *conn){ FlapFrame *frame; frame = flap_frame_new(od, 0x05, 0); flap_connection_send(conn, frame); /* clean out SNACs over 60sec old */ aim_cleansnacs(od, 60);}/** * Allocate a new empty connection structure. * * @param od The oscar session associated with this connection. * @param type Type of connection to create * * @return Returns the new connection structure. */FlapConnection *flap_connection_new(OscarData *od, int type){ FlapConnection *conn; conn = g_new0(FlapConnection, 1); conn->od = od; conn->buffer_outgoing = purple_circ_buffer_new(0); conn->fd = -1; conn->subtype = -1; conn->type = type; conn->queued_snacs = g_queue_new(); od->oscar_connections = g_slist_prepend(od->oscar_connections, conn); return conn;}/** * Close (but not free) a connection. * * This cancels any currently pending connection attempt, * closes any open fd and frees the auth cookie. * * @param conn The connection to close. */voidflap_connection_close(OscarData *od, FlapConnection *conn){ if (conn->connect_data != NULL) { purple_proxy_connect_cancel(conn->connect_data); conn->connect_data = NULL; } if (conn->connect_data != NULL) { if (conn->type == SNAC_FAMILY_CHAT) { oscar_chat_destroy(conn->new_conn_data); conn->connect_data = NULL; } } if (conn->fd >= 0) { if (conn->type == SNAC_FAMILY_LOCATE) flap_connection_send_close(od, conn); close(conn->fd); conn->fd = -1; } if (conn->watcher_incoming != 0) { purple_input_remove(conn->watcher_incoming); conn->watcher_incoming = 0; } if (conn->watcher_outgoing != 0) { purple_input_remove(conn->watcher_outgoing); conn->watcher_outgoing = 0; } g_free(conn->buffer_incoming.data.data); conn->buffer_incoming.data.data = NULL; purple_circ_buffer_destroy(conn->buffer_outgoing); conn->buffer_outgoing = NULL;}static voidflap_connection_destroy_rateclass(struct rateclass *rateclass){ g_hash_table_destroy(rateclass->members); g_free(rateclass);}/** * Free a FlapFrame * * @param frame The frame to free. */static voidflap_frame_destroy(FlapFrame *frame){ g_free(frame->data.data); g_free(frame);}static gbooleanflap_connection_destroy_cb(gpointer data){ FlapConnection *conn; OscarData *od; PurpleAccount *account; conn = data; od = conn->od; account = (PURPLE_CONNECTION_IS_VALID(od->gc) ? purple_connection_get_account(od->gc) : NULL); purple_debug_info("oscar", "Destroying oscar connection of " "type 0x%04hx\n", conn->type); od->oscar_connections = g_slist_remove(od->oscar_connections, conn); /* * TODO: If we don't have a SNAC_FAMILY_LOCATE connection then * we should try to request one instead of disconnecting. */ if (account && !account->disconnecting && ((od->oscar_connections == NULL) || (!flap_connection_getbytype(od, SNAC_FAMILY_LOCATE)))) { /* No more FLAP connections! Sign off this PurpleConnection! */ gchar *tmp; if (conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_CLOSED) tmp = g_strdup(_("Server closed the connection.")); else if (conn->disconnect_reason == OSCAR_DISCONNECT_LOST_CONNECTION) tmp = g_strdup_printf(_("Lost connection with server:\n%s"), conn->error_message); else if (conn->disconnect_reason == OSCAR_DISCONNECT_INVALID_DATA) tmp = g_strdup(_("Received invalid data on connection with server.")); else if (conn->disconnect_reason == OSCAR_DISCONNECT_COULD_NOT_CONNECT) tmp = g_strdup_printf(_("Could not establish a connection with the server:\n%s"), conn->error_message); else /* * We shouldn't print a message for some disconnect_reasons. * Like OSCAR_DISCONNECT_LOCAL_CLOSED. */ tmp = NULL; if (tmp != NULL) { purple_connection_error(od->gc, tmp); g_free(tmp); } } flap_connection_close(od, conn); g_free(conn->error_message); g_free(conn->cookie); /* * Free conn->internal, if necessary */ if (conn->type == SNAC_FAMILY_CHAT) flap_connection_destroy_chat(od, conn); g_slist_free(conn->groups); while (conn->rateclasses != NULL) { flap_connection_destroy_rateclass(conn->rateclasses->data); conn->rateclasses = g_slist_delete_link(conn->rateclasses, conn->rateclasses); } while (!g_queue_is_empty(conn->queued_snacs)) { QueuedSnac *queued_snac; queued_snac = g_queue_pop_head(conn->queued_snacs); flap_frame_destroy(queued_snac->frame); g_free(queued_snac); } g_queue_free(conn->queued_snacs); if (conn->queued_timeout > 0) purple_timeout_remove(conn->queued_timeout); g_free(conn); return FALSE;}/** * See the comments for the parameters of * flap_connection_schedule_destroy(). */voidflap_connection_destroy(FlapConnection *conn, OscarDisconnectReason reason, const gchar *error_message){ if (conn->destroy_timeout != 0) purple_timeout_remove(conn->destroy_timeout); conn->disconnect_reason = reason; g_free(conn->error_message); conn->error_message = g_strdup(error_message); flap_connection_destroy_cb(conn);}/** * Schedule Purple to destroy the given FlapConnection as soon as we * return control back to the program's main loop. We must do this * if we want to destroy the connection but we are still using it * for some reason. * * @param reason The reason for the disconnection. * @param error_message A brief error message that gives more detail * regarding the reason for the disconnecting. This should * be NULL for everything except OSCAR_DISCONNECT_LOST_CONNECTION, * in which case it should contain the value of strerror(errno), * and OSCAR_DISCONNECT_COULD_NOT_CONNECT, in which case it * should contain the error_message passed back from the call * to purple_proxy_connect(). */voidflap_connection_schedule_destroy(FlapConnection *conn, OscarDisconnectReason reason, const gchar *error_message){ if (conn->destroy_timeout != 0) /* Already taken care of */ return; purple_debug_info("oscar", "Scheduling destruction of FLAP " "connection of type 0x%04hx\n", conn->type); conn->disconnect_reason = reason; g_free(conn->error_message); conn->error_message = g_strdup(error_message); conn->destroy_timeout = purple_timeout_add(0, flap_connection_destroy_cb, conn);}/** * In OSCAR, every connection has a set of SNAC groups associated * with it. These are the groups that you can send over this connection * without being guaranteed a "Not supported" SNAC error. * * The grand theory of things says that these associations transcend * what libfaim calls "connection types" (conn->type). You can probably * see the elegance here, but since I want to revel in it for a bit, you * get to hear it all spelled out. * * So let us say that you have your core BOS connection running. One
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -