📄 server.c
字号:
/* X-Chat * Copyright (C) 1998 Peter Zelezny. * * 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 <stdio.h>#include <string.h>#include <stdlib.h>#include <unistd.h>#include <errno.h>#include <fcntl.h>#define WANTSOCKET#define WANTARPA#include "inet.h"#ifndef WIN32#include <signal.h>#include <sys/wait.h>#else#include <winbase.h>#endif#include "xchat.h"#include "plugin.h"#include "fe.h"#include "cfgfiles.h"#include "network.h"#include "notify.h"#include "xchatc.h"#include "inbound.h"#include "outbound.h"#include "text.h"#include "util.h"#include "server.h"#ifdef USE_OPENSSL#include <openssl/ssl.h> /* SSL_() */#include <openssl/err.h> /* ERR_() */#include "ssl.h"#endif#ifdef USE_JCODE#include "jcode.h"#endif#ifdef WIN32#include "identd.c"#endif#ifdef USE_OPENSSLextern SSL_CTX *ctx; /* xchat.c *//* local variables */static struct session *g_sess = NULL;#endifstatic void server_stopconnecting (server * serv);static gbooleanread_data (GIOChannel *source, GIOCondition condition, server *serv);static intclose_socket_cb (int sok){ closesocket (sok); return 0;}static voidclose_socket (int sok){ /* close the socket in 5 seconds so the QUIT message is not lost */ fe_timeout_add (5000, close_socket_cb, (void *) sok);}static voidserver_connected (server * serv){ char hostname[256]; char outbuf[512]; serv->ping_recv = time (0);#ifdef WIN32 identd_start ();#else sprintf (outbuf, "%s/auth/xchat_auth", g_get_home_dir ()); if (access (outbuf, X_OK) == 0) { sprintf (outbuf, "/exec %s/auth/xchat_auth %s", g_get_home_dir (), prefs.username); handle_command (outbuf, serv->front_session, FALSE, FALSE); }#endif serv->connected = TRUE; serv->iotag = fe_input_add (serv->sok, 1, 0, 1, read_data, serv); if (!serv->no_login) { EMIT_SIGNAL (XP_TE_CONNECTED, serv->front_session, NULL, NULL, NULL, NULL, 0); if (serv->password[0]) { sprintf (outbuf, "PASS %s\r\n", serv->password); tcp_send (serv, outbuf); } gethostname (hostname, sizeof (hostname) - 1); hostname[sizeof (hostname) - 1] = 0; if (hostname[0] == 0) strcpy (hostname, "0"); snprintf (outbuf, sizeof (outbuf), "NICK %s\r\n" "USER %s %s %s :%s\r\n", serv->nick, prefs.username, hostname, serv->servername, prefs.realname); tcp_send (serv, outbuf); } else { EMIT_SIGNAL (XP_TE_SERVERCONNECTED, serv->front_session, NULL, NULL, NULL, NULL, 0); } set_nonblocking (serv->sok); set_server_name (serv, serv->servername);}#ifdef USE_OPENSSL#define SSLTMOUT 10 /* seconds */voidssl_cb_info (SSL * s, int where, int ret){/* char buf[128];*/ return; /* FIXME: make debug level adjustable in serverlist or settings *//* snprintf (buf, sizeof (buf), "%s (%d)", SSL_state_string_long (s), where); if (g_sess) EMIT_SIGNAL (XP_TE_SERVTEXT, g_sess, buf, NULL, NULL, NULL, 0); else fprintf (stderr, "%s\n", buf);*/}static intssl_cb_verify (int ok, X509_STORE_CTX * ctx){ char subject[256]; char issuer[256]; char buf[512]; X509_NAME_oneline (X509_get_subject_name (ctx->current_cert), subject, sizeof (subject)); X509_NAME_oneline (X509_get_issuer_name (ctx->current_cert), issuer, sizeof (issuer)); snprintf (buf, sizeof (buf), "* Subject: %s", subject); EMIT_SIGNAL (XP_TE_SERVTEXT, g_sess, buf, NULL, NULL, NULL, 0); snprintf (buf, sizeof (buf), "* Issuer: %s", issuer); EMIT_SIGNAL (XP_TE_SERVTEXT, g_sess, buf, NULL, NULL, NULL, 0); return (TRUE); /* always ok */}static intssl_do_connect (server * serv){ char buf[128]; g_sess = serv->front_session; if (SSL_connect (serv->ssl) <= 0) { char err_buf[128]; int err; g_sess = NULL; if ((err = ERR_get_error ()) > 0) { ERR_error_string (err, err_buf); snprintf (buf, sizeof (buf), "(%d) %s", err, err_buf); EMIT_SIGNAL (XP_TE_CONNFAIL, serv->front_session, buf, NULL, NULL, NULL, 0); server_cleanup (serv); if (prefs.autoreconnectonfail) auto_reconnect (serv, FALSE, -1); return (0); /* remove it (0) */ } } g_sess = NULL; if (SSL_is_init_finished (serv->ssl)) { struct cert_info cert_info; struct chiper_info *chiper_info; int verify_error; int i; if (!_SSL_get_cert_info (&cert_info, serv->ssl)) { snprintf (buf, sizeof (buf), "* Certification info:"); EMIT_SIGNAL (XP_TE_SERVTEXT, serv->front_session, buf, NULL, NULL, NULL, 0); snprintf (buf, sizeof (buf), " Subject:"); EMIT_SIGNAL (XP_TE_SERVTEXT, serv->front_session, buf, NULL, NULL, NULL, 0); for (i = 0; cert_info.subject_word[i]; i++) { snprintf (buf, sizeof (buf), " %s", cert_info.subject_word[i]); EMIT_SIGNAL (XP_TE_SERVTEXT, serv->front_session, buf, NULL, NULL, NULL, 0); } snprintf (buf, sizeof (buf), " Issuer:"); EMIT_SIGNAL (XP_TE_SERVTEXT, serv->front_session, buf, NULL, NULL, NULL, 0); for (i = 0; cert_info.issuer_word[i]; i++) { snprintf (buf, sizeof (buf), " %s", cert_info.issuer_word[i]); EMIT_SIGNAL (XP_TE_SERVTEXT, serv->front_session, buf, NULL, NULL, NULL, 0); } snprintf (buf, sizeof (buf), " Public key algorithm: %s (%d bits)", cert_info.algorithm, cert_info.algorithm_bits); EMIT_SIGNAL (XP_TE_SERVTEXT, serv->front_session, buf, NULL, NULL, NULL, 0); if (cert_info.rsa_tmp_bits) { snprintf (buf, sizeof (buf), " Public key algorithm uses ephemeral key with %d bits", cert_info.rsa_tmp_bits); EMIT_SIGNAL (XP_TE_SERVTEXT, serv->front_session, buf, NULL, NULL, NULL, 0); } snprintf (buf, sizeof (buf), " Sign algorithm %s (%d bits)", cert_info.sign_algorithm, cert_info.sign_algorithm_bits); EMIT_SIGNAL (XP_TE_SERVTEXT, serv->front_session, buf, NULL, NULL, NULL, 0); snprintf (buf, sizeof (buf), " Valid since %s to %s", cert_info.notbefore, cert_info.notafter); EMIT_SIGNAL (XP_TE_SERVTEXT, serv->front_session, buf, NULL, NULL, NULL, 0); } else { snprintf (buf, sizeof (buf), " * No Certificate"); EMIT_SIGNAL (XP_TE_SERVTEXT, serv->front_session, buf, NULL, NULL, NULL, 0); } chiper_info = _SSL_get_cipher_info (serv->ssl); /* static buffer */ snprintf (buf, sizeof (buf), "* Chiper info:"); EMIT_SIGNAL (XP_TE_SERVTEXT, serv->front_session, buf, NULL, NULL, NULL, 0); snprintf (buf, sizeof (buf), " Version: %s, cipher %s (%u bits)", chiper_info->version, chiper_info->chiper, chiper_info->chiper_bits); EMIT_SIGNAL (XP_TE_SERVTEXT, serv->front_session, buf, NULL, NULL, NULL, 0); verify_error = SSL_get_verify_result (serv->ssl); switch (verify_error) { case X509_V_OK: /* snprintf (buf, sizeof (buf), "* Verify OK (?)"); */ /* EMIT_SIGNAL (XP_TE_SERVTEXT, serv->front_session, buf, NULL, NULL, NULL, 0); */ break; case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: if (serv->accept_invalid_cert) { snprintf (buf, sizeof (buf), "* Verify E: %s.? (%d) -- Ignored", X509_verify_cert_error_string (verify_error), verify_error); EMIT_SIGNAL (XP_TE_SERVTEXT, serv->front_session, buf, NULL, NULL, NULL, 0); break; } default: snprintf (buf, sizeof (buf), "%s.? (%d)", X509_verify_cert_error_string (verify_error), verify_error); EMIT_SIGNAL (XP_TE_CONNFAIL, serv->front_session, buf, NULL, NULL, NULL, 0); server_cleanup (serv); return (0); } server_stopconnecting (serv); /* activate gtk poll */ server_connected (serv); return (0); /* remove it (0) */ } else { if (serv->ssl->session->time + SSLTMOUT < time (NULL)) { snprintf (buf, sizeof (buf), "SSL handshake timed out"); EMIT_SIGNAL (XP_TE_CONNFAIL, serv->front_session, buf, NULL, NULL, NULL, 0); server_cleanup (serv); /* ->connecting = FALSE */ if (prefs.autoreconnectonfail) auto_reconnect (serv, FALSE, -1); return (0); /* remove it (0) */ } return (1); /* call it more (1) */ }}#endifstatic inttimeout_auto_reconnect (struct server *serv){ if (is_server (serv)) /* make sure it hasnt been closed during the delay */ { serv->recondelay_tag = 0; if (!serv->connected && !serv->connecting && serv->front_session) { connect_server (serv->front_session, serv->hostname, serv->port, FALSE); } } return 0; /* returning 0 should remove the timeout handler */}voidauto_reconnect (struct server *serv, int send_quit, int err){ session *s; GSList *list; int del; if (serv->front_session == NULL) return; list = sess_list; while (list) /* make sure auto rejoin can work */ { s = list->data; if (s->type == SESS_CHANNEL && s->channel[0]) { strcpy (s->waitchannel, s->channel); strcpy (s->willjoinchannel, s->channel); } list = list->next; } if (serv->connected) disconnect_server (serv->front_session, send_quit, err); del = prefs.recon_delay * 1000; if (del < 1000) del = 500; /* so it doesn't block the gui */#ifndef WIN32 if (err == 0 || err == ECONNRESET || err == ETIMEDOUT)#else if (err == 0 || err == WSAECONNRESET || err == WSAETIMEDOUT)#endif serv->reconnect_away = serv->is_away; serv->recondelay_tag = fe_timeout_add (del, timeout_auto_reconnect, serv);}static gbooleanread_data (GIOChannel *source, GIOCondition condition, server *serv){ int sok = serv->sok; int error, i, len; char lbuf[2050]; char *temp;#ifdef USE_JCODE char *jtemp;#endif while (1) { if (!EMIT_SIGNAL (XP_IF_RECV, &len, (void *) sok, &lbuf, (void *) ((sizeof (lbuf)) - 2), NULL, 0)) {#ifdef USE_OPENSSL if (!serv->ssl)#endif len = recv (sok, lbuf, sizeof (lbuf) - 2, 0);#ifdef USE_OPENSSL else len = _SSL_recv (serv->ssl, lbuf, sizeof (lbuf) - 2);#endif } if (len < 1) { if (len < 0) { if (would_block_again ()) return TRUE; error = sock_error (); } else { error = 0; } if (prefs.autoreconnect) auto_reconnect (serv, FALSE, error); else disconnect_server (serv->front_session, FALSE, error); return TRUE; } else { i = 0; lbuf[len] = 0; while (i < len) { switch (lbuf[i]) { case '\r': break; case '\n': serv->linebuf[serv->pos] = 0;#ifdef USE_TRANS if (prefs.use_trans) serv2user (serv->linebuf);#endif if (prefs.stripcolor) { temp = strip_color (serv->linebuf);#ifdef USE_JCODE if (prefs.kanji_conv) { jtemp = kanji_conv_to_locale (temp); if (jtemp) { process_line (serv, jtemp); free (jtemp); } else process_line (serv, temp); } else process_line (serv, temp);#else process_line (serv, temp);#endif free (temp); } else {#ifdef USE_JCODE if (prefs.kanji_conv) { jtemp = kanji_conv_to_locale (serv->linebuf); if (jtemp) { process_line (serv, jtemp); free (jtemp); } else process_line (serv, serv->linebuf); } else process_line (serv, serv->linebuf);#else process_line (serv, serv->linebuf);#endif } serv->pos = 0; break; default: serv->linebuf[serv->pos] = lbuf[i]; if (serv->pos > 519) fprintf (stderr, "*** XCHAT WARNING: Buffer overflow - shit server!\n"); else serv->pos++; } i++; } } }}voidflush_server_queue (struct server *serv){ list_free (&serv->outbound_queue); serv->sendq_len = 0; fe_set_throttle (serv);}#ifdef WIN32static intwaitline2 (GIOChannel *source, char *buf, int bufsize){ int i = 0; int len; while (1) { if (g_io_channel_read (source, &buf[i], 1, &len) != G_IO_ERROR_NONE) return -1; if (buf[i] == '\n' || bufsize == i + 1) { buf[i] = 0; return i; } i++; }}#else#define waitline2(source,buf,size) waitline(serv->childread,buf,size)#endifstatic gbooleanconnected_signal (GIOChannel *source, GIOCondition condition, server *serv){ session *sess = serv->front_session; char tbuf[128]; char outbuf[512]; char host[100]; char ip[100]; waitline2 (source, tbuf, sizeof tbuf); switch (tbuf[0]) { case '1': /* unknown host */ server_stopconnecting (serv); closesocket (serv->sok4); if (serv->sok6 >= 0) closesocket (serv->sok6); EMIT_SIGNAL (XP_TE_UKNHOST, sess, NULL, NULL, NULL, NULL, 0); if (prefs.autoreconnectonfail) auto_reconnect (serv, FALSE, -1); break; case '2': /* connection failed */ waitline2 (source, tbuf, sizeof tbuf); server_stopconnecting (serv); closesocket (serv->sok4); if (serv->sok6 >= 0) closesocket (serv->sok6); EMIT_SIGNAL (XP_TE_CONNFAIL, sess, errorstring (atoi (tbuf)), NULL,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -