📄 connection.c
字号:
/* * Copyright (C) 2000,2001 Onlyer (onlyer@263.net) * * 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. */#include "common/setup_before.h"#include "setup.h"#include <ctype.h>#ifdef HAVE_STRING_H# include <string.h>#else# ifdef HAVE_STRINGS_H# include <strings.h># endif# ifdef HAVE_MEMORY_H# include <memory.h># endif#endif#ifdef STDC_HEADERS# include <stdlib.h>#else# ifdef HAVE_MALLOC_H# include <malloc.h># endif#endif#include "compat/memcpy.h"#include "compat/strdup.h"#ifdef HAVE_UNISTD_H# include <unistd.h>#endif#ifdef HAVE_SYS_TYPES_H# include <sys/types.h>#endif#ifdef HAVE_SYS_SOCKET_H# include <sys/socket.h>#endif#include "compat/psock.h"#ifdef HAVE_NETINET_IN_H# include <netinet/in.h>#endif#include "compat/netinet_in.h"#ifdef HAVE_LIMITS_H# include <limits.h>#endif#include "compat/char_bit.h"#ifdef TIME_WITH_SYS_TIME# include <time.h># include <sys/time.h>#else# ifdef HAVE_SYS_TIME_H# include <sys/time.h># else# include <time.h># endif#endif#ifdef HAVE_ASSERT_H# include <assert.h>#endif#include "compat/psock.h"#include "compat/strcasecmp.h"#include "connection.h"#include "game.h"#include "gamequeue.h"#include "prefs.h"#include "d2gs.h"#include "net.h"#include "s2s.h"#include "handle_d2gs.h"#include "handle_d2cs.h"#include "handle_init.h"#include "handle_bnetd.h"#include "d2charfile.h"#include "common/fdwatch.h"#include "common/addr.h"#include "common/introtate.h"#include "common/network.h"#include "common/packet.h"#include "common/hashtable.h"#include "common/queue.h"#include "common/eventlog.h"#include "common/xalloc.h"#include "common/setup_after.h"static t_hashtable * connlist_head=NULL;static t_hashtable * conn_charname_list_head=NULL;static t_list * connlist_dead=NULL;static unsigned int total_connection=0;static int conn_handle_connecting(t_connection * c);static t_packet * conn_create_packet(t_connection * c);static int conn_handle_packet(t_connection * c, t_packet * packet);static int conn_handle_read(t_connection * c);static int conn_handle_write(t_connection * c);static unsigned int conn_charname_hash(char const * charname);static unsigned int conn_sessionnum_hash(unsigned int sessionnum);static unsigned int conn_sessionnum_hash(unsigned int sessionnum){ return sessionnum;}static unsigned int conn_charname_hash(char const * charname){ unsigned int hash; unsigned int i, len, pos; unsigned int ch; ASSERT(charname,0); len=strlen(charname); for (hash=0, i=0, pos=0; i<len; i++) { if (isascii((int)charname[i])) { ch=(unsigned int)(unsigned char)tolower((int)charname[i]); } else { ch=(unsigned int)(unsigned char)charname[i]; } hash ^= ROTL(ch,pos,sizeof(unsigned int) * CHAR_BIT); pos += CHAR_BIT-1; } return hash;}extern t_hashtable * d2cs_connlist(void){ return connlist_head;}extern int d2cs_connlist_create(void){ if (!(connlist_head=hashtable_create(200))) return -1; if (!(conn_charname_list_head=hashtable_create(200))) return -1; return 0;}extern int d2cs_connlist_destroy(void){ t_connection * c; t_elem * curr; if (connlist_dead) { d2cs_connlist_reap(); if (list_destroy(connlist_dead)) eventlog(eventlog_level_error,__FUNCTION__,"error destroy conndead list"); connlist_dead = NULL; } BEGIN_HASHTABLE_TRAVERSE_DATA(connlist_head, c) { d2cs_conn_destroy(c,&curr); } END_HASHTABLE_TRAVERSE_DATA() if (hashtable_destroy(connlist_head)<0) { eventlog(eventlog_level_error,__FUNCTION__,"error destroy connection list"); return -1; } connlist_head=NULL; if (hashtable_destroy(conn_charname_list_head)<0) { eventlog(eventlog_level_error,__FUNCTION__,"error destroy connection charname list"); return -1; } conn_charname_list_head=NULL; return 0;}extern int d2cs_connlist_reap(void){ t_connection * c; if (!connlist_dead) return 0; BEGIN_LIST_TRAVERSE_DATA(connlist_dead, c) { d2cs_conn_destroy(c,&curr_elem_); } END_LIST_TRAVERSE_DATA() return 0;}extern int conn_check_multilogin(t_connection const * c,char const * charname){ t_connection * conn; ASSERT(charname,-1); if (!prefs_check_multilogin()) return 0; if (gamelist_find_character(charname)) { return -1; } conn=d2cs_connlist_find_connection_by_charname(charname); if (conn && conn!=c) { return -1; } return 0;}extern t_connection * d2cs_connlist_find_connection_by_sessionnum(unsigned int sessionnum){ t_connection * c; t_entry * curr; unsigned int hash; hash=conn_sessionnum_hash(sessionnum); HASHTABLE_TRAVERSE_MATCHING(connlist_head,curr,hash) { if (!(c=entry_get_data(curr))) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection in list"); } else if (c->sessionnum==sessionnum) { hashtable_entry_release(curr); return c; } } return NULL;}extern t_connection * d2cs_connlist_find_connection_by_charname(char const * charname){ t_entry * curr; t_connection * c; unsigned int hash; hash=conn_charname_hash(charname); HASHTABLE_TRAVERSE_MATCHING(connlist_head,curr,hash) { if (!(c=entry_get_data(curr))) { eventlog(eventlog_level_error,__FUNCTION__,"got NULL connection in list"); } else { if (!c->charname) continue; if (!strcmp_charname(c->charname,charname)) { hashtable_entry_release(curr); return c; } } } return NULL;}static t_packet * conn_create_packet(t_connection * c){ t_packet * packet; switch (c->class) { CASE(conn_class_init, packet=packet_create(packet_class_init)); CASE(conn_class_d2cs, packet=packet_create(packet_class_d2cs)); CASE(conn_class_d2gs, packet=packet_create(packet_class_d2gs)); CASE(conn_class_bnetd, packet=packet_create(packet_class_d2cs_bnetd)); default: eventlog(eventlog_level_error,__FUNCTION__,"got bad connection class %d",c->class); return NULL; } if (!packet) { eventlog(eventlog_level_error,__FUNCTION__,"error create packet"); return NULL; } d2cs_conn_set_in_queue(c,packet); return packet;}static int conn_handle_connecting(t_connection * c){ int retval; if (net_check_connected(c->sock)<0) { eventlog(eventlog_level_warn,__FUNCTION__,"can not connect to %s",addr_num_to_addr_str(c->addr, c->port)); return -1; } eventlog(eventlog_level_info,__FUNCTION__,"connected to %s",addr_num_to_addr_str(c->addr, c->port)); c->state=conn_state_init; /* this is a kind of hack to not update fd but updating breaks kqueue * and the clean fix would require a cache a userland copy of the kernel * kqueue fds, considering that it also doesnt brake anything else should do * for the moment fdwatch_update_fd(c->sock, fdwatch_type_read); */ switch (c->class) { case conn_class_bnetd: retval=handle_bnetd_init(c); break; default: eventlog(eventlog_level_error,__FUNCTION__,"got bad connection class %d",c->class); return -1; } return retval;}static int conn_handle_packet(t_connection * c, t_packet * packet){ int retval; switch (c->class) { CASE (conn_class_init, retval=d2cs_handle_init_packet(c,packet)); CASE (conn_class_d2cs, retval=d2cs_handle_d2cs_packet(c,packet)); CASE (conn_class_d2gs, retval=handle_d2gs_packet(c,packet)); CASE (conn_class_bnetd, retval=handle_bnetd_packet(c,packet)); default: eventlog(eventlog_level_error,__FUNCTION__,"got bad connection class %d (close connection)",c->class); retval=-1; break; } return retval;}static int conn_handle_read(t_connection * c){ t_packet * packet; int retval; packet = d2cs_conn_get_in_queue(c); if (!packet) { packet = conn_create_packet(c); if (!packet) return -1; c->insize=0; } switch (net_recv_packet(c->sock,packet,&c->insize)) { case -1: retval=-1; break; case 0: retval=0; break; case 1: c->insize=0; d2cs_conn_set_in_queue(c,NULL); retval=conn_handle_packet(c,packet); packet_del_ref(packet); break; default: retval=0; break; } return retval;}static int conn_handle_write(t_connection * c){ t_packet * packet; int retval; if (c->state==conn_state_connecting) { return conn_handle_connecting(c); } if (!(packet=conn_peek_outqueue(c))) return 0; switch (net_send_packet(c->sock, packet, &c->outsize)) { case -1: retval=-1; break; case 0: retval=0; break; case 1: c->outsize=0; packet=conn_pull_outqueue(c); packet_del_ref(packet); retval=0; break; default: retval = -1; } return retval;}extern int conn_handle_socket(t_connection * c){ time_t now; ASSERT(c,-1); now=time(NULL); if (c->socket_flag & SOCKET_FLAG_READ) { if (conn_handle_read(c)<0) return -1; c->last_active=now; } if (c->socket_flag & SOCKET_FLAG_WRITE) { if (conn_handle_write(c)<0) return -1; c->last_active=now; } c->socket_flag=0; return 0;}extern int connlist_check_timeout(void){ t_connection * c; time_t now; now=time(NULL); BEGIN_HASHTABLE_TRAVERSE_DATA(connlist_head, c) { switch (c->class) { case conn_class_d2cs: if (prefs_get_idletime() && (now - c->last_active > prefs_get_idletime())) { eventlog(eventlog_level_info,__FUNCTION__,"client %d idled too long time, destroy it",c->sessionnum); d2cs_conn_set_state(c,conn_state_destroy); } break; case conn_class_d2gs: if (prefs_get_s2s_idletime() && now - c->last_active > prefs_get_s2s_idletime()) { eventlog(eventlog_level_info,__FUNCTION__,"server %d timed out",c->sessionnum); d2cs_conn_set_state(c,conn_state_destroy); } break; case conn_class_bnetd: break; default: break; } } END_HASHTABLE_TRAVERSE_DATA()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -