1
0
forked from bip/bip
bip/src/bip.c

1308 lines
30 KiB
C
Raw Normal View History

2005-04-28 10:26:44 +02:00
/*
* $Id: bip.c,v 1.39 2005/04/21 06:58:50 nohar Exp $
*
* This file is part of the bip project
2005-10-09 13:47:20 +02:00
* Copyright (C) 2004 2005 Arnaud Cornet and Lo<EFBFBD>c Gomez
2005-04-28 10:26:44 +02:00
*
* 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.
* See the file "COPYING" for the exact licensing terms.
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdarg.h>
#include "irc.h"
#include "conf.h"
#include "tuple.h"
#include "log.h"
#include "irc.h"
#include "bip.h"
#include "line.h"
int sighup = 0;
2005-04-28 10:26:44 +02:00
char *conf_log_root;
char *conf_log_format;
int conf_log_level;
char *conf_ip;
unsigned short conf_port;
int conf_css;
#ifdef HAVE_LIBSSL
char *conf_ssl_certfile;
#endif
int conf_daemonize;
char *conf_pid_file;
char *conf_biphome;
/* log options, for sure the trickiest :) */
/* no backlog at all */
2007-01-12 23:19:29 +01:00
int conf_backlog = 0;
extern int conf_memlog;
int conf_log = 0;
int conf_log_system = 0;
2007-01-12 23:19:29 +01:00
int conf_log_sync_interval = 0;
2005-04-28 10:26:44 +02:00
2005-05-26 17:36:15 +02:00
list_t *parse_conf(FILE *file);
static void conf_die(char *fmt, ...);
2005-08-25 10:17:10 +02:00
#ifdef HAVE_LIBSSL
int adm_trust(struct link_client *ic, struct line *line);
2005-08-25 10:17:10 +02:00
#endif
static char *get_tuple_value(list_t *tuple_l, int lex);
2005-04-28 10:26:44 +02:00
static void hash_binary(char *hex, unsigned char **password, unsigned int *seed)
{
unsigned char *md5;
unsigned int buf;
int i;
if (strlen(hex) != 40)
fatal("Incorrect password format %s\n", hex);
2005-10-09 14:40:38 +02:00
2005-04-28 10:26:44 +02:00
md5 = malloc(20);
for (i = 0; i < 20; i++) {
sscanf(hex + 2 * i, "%02x", &buf);
md5[i] = buf;
}
*seed = 0;
sscanf(hex, "%02x", &buf);
*seed |= buf << 24;
sscanf(hex + 2, "%02x", &buf);
*seed |= buf << 16;
sscanf(hex + 2 * 2, "%02x", &buf);
*seed |= buf << 8;
sscanf(hex + 2 * 3, "%02x", &buf);
*seed |= buf;
*password = md5;
}
static int add_server(struct server *s, list_t *data)
2005-04-28 10:26:44 +02:00
{
struct tuple *t;
2005-05-26 17:36:15 +02:00
2005-04-28 10:26:44 +02:00
s->port = 6667; /* default port */
while ((t = list_remove_first(data))) {
switch (t->type) {
case LEX_HOST:
s->host = t->pdata;
break;
case LEX_PORT:
s->port = t->ndata;
break;
default:
fatal("Config error in server block (%d)", t->type);
}
}
2005-05-26 17:36:15 +02:00
if (!s->host) {
free(s);
conf_die("Server conf: host not set");
return 0;
}
return 1;
2005-04-28 10:26:44 +02:00
}
2005-05-26 17:36:15 +02:00
#define ERRBUFSZ 128
2005-04-28 10:26:44 +02:00
extern list_t *root_list;
int yyparse();
int conf_error;
char conf_errstr[ERRBUFSZ];
2005-05-26 17:36:15 +02:00
static void conf_start(void)
{
conf_error = 0;
}
2005-04-28 10:26:44 +02:00
2005-05-26 17:36:15 +02:00
static void conf_die(char *fmt, ...)
2005-04-28 10:26:44 +02:00
{
2005-05-26 17:36:15 +02:00
va_list ap;
va_start(ap, fmt);
vsnprintf(conf_errstr, ERRBUFSZ, fmt, ap);
conf_errstr[ERRBUFSZ - 1] = 0;
2005-07-09 14:55:01 +02:00
conf_error = 1;
2005-05-26 17:36:15 +02:00
va_end(ap);
2005-04-28 10:26:44 +02:00
}
2005-05-26 17:36:15 +02:00
2005-04-28 10:26:44 +02:00
FILE *conf_global_log_file;
static pid_t daemonize(void)
{
switch (fork()) {
case -1:
fatal("Fork failed");
break;
case 0:
break;
default:
2006-10-21 12:36:56 +02:00
_exit(0);
2005-04-28 10:26:44 +02:00
}
2006-10-21 12:36:56 +02:00
2005-04-28 10:26:44 +02:00
if (setsid() < 0)
fatal("setsid() failed");
2005-05-21 16:23:05 +02:00
2006-10-21 12:36:56 +02:00
switch (fork()) {
case -1:
fatal("Fork failed");
break;
case 0:
break;
default:
_exit(0);
}
2005-04-28 10:26:44 +02:00
close(0);
close(1);
close(2);
/* This better be the very last action since fatal makes use of
* conf_global_log_file */
return getpid();
}
/* RACE CONDITION! */
int do_pid_stuff(void)
{
char hname[1024];
char longpath[1024];
FILE *f;
try_again:
f = fopen(conf_pid_file, "r");
if (f)
goto pid_is_there;
if (gethostname(hname, 1023) == -1)
fatal("%s %s", "gethostname", strerror(errno));
snprintf(longpath, 1023, "%s.%s.%ld", conf_pid_file, hname,
(long unsigned int)getpid());
int fd;
if ((fd = open(longpath, O_CREAT|O_WRONLY, S_IWUSR|S_IRUSR)) == -1)
fatal("%s %s", "open", strerror(errno));
if (link(longpath, conf_pid_file) == -1) {
struct stat buf;
if (stat(longpath, &buf) == -1) {
if (buf.st_nlink != 2) {
f = fopen(conf_pid_file, "r");
goto pid_is_there;
}
}
}
unlink(longpath);
return fd;
pid_is_there:
{
pid_t pid;
long unsigned int p;
if (f) {
int c = fscanf(f, "%ld", &p);
pid = p;
if (c != 1 || p == 0) {
mylog(LOG_INFO, "pid file found but invalid "
"data inside. Continuing...\n");
if (unlink(conf_pid_file)) {
fatal("Cannot delete pid file '%s', "
"check permissions.\n",
conf_pid_file);
}
goto try_again;
}
} else
pid = 0;
int kr = kill(pid, 0);
if (kr == -1 && (errno == ESRCH || errno == EPERM)) {
/* that's not bip! */
fclose(f);
if (unlink(conf_pid_file)) {
fatal("Cannot delete pid file '%s', check "
"permissions.\n",
conf_pid_file);
}
goto try_again;
}
if (pid)
mylog(LOG_INFO, "pid file found (pid %ld).", pid);
mylog(LOG_STD, "Another instance of bip is certainly runing.");
mylog(LOG_STD, "If you are sure this is not the case remove"
2005-04-28 10:26:44 +02:00
" %s.", conf_pid_file);
exit(2);
}
return 0;
}
#define S_CONF "/.bip/bip.conf"
static void usage(char *name)
{
printf(
"Usage: %s [-f config_file] [-h] [-n]\n"
" -f config_file: Use config_file as the configuration file\n"
" If no config file is given %s will try to open ~" S_CONF "\n"
" -n: Don't daemonize, log in stderr\n"
" -h: This help\n", name, name);
exit(1);
}
void reload_config(int i)
{
2005-08-01 13:24:10 +02:00
(void)i;
2005-04-28 10:26:44 +02:00
sighup = 1;
}
2007-05-26 12:31:44 +02:00
bip_t *_bip;
2005-05-27 18:50:26 +02:00
2005-04-28 10:26:44 +02:00
void bad_quit(int i)
{
2005-05-27 18:50:26 +02:00
list_iterator_t it;
2007-05-26 12:31:44 +02:00
for (list_it_init(&_bip->link_list, &it); list_it_item(&it);
2005-05-27 18:50:26 +02:00
list_it_next(&it)) {
2007-05-26 12:31:44 +02:00
struct link *l = list_it_item(&it);
struct link_server *ls = l->l_server;
if (ls && l->s_state == IRCS_CONNECTED) {
write_line_fast(CONN(ls), "QUIT :Coyote finally "
2005-05-27 18:50:26 +02:00
"caught me\r\n");
}
}
2005-04-28 10:26:44 +02:00
unlink(conf_pid_file);
exit(i);
}
static int add_network(bip_t *bip, list_t *data)
2005-04-28 10:26:44 +02:00
{
struct tuple *t;
struct network *n;
int i;
2005-04-28 10:26:44 +02:00
char *name = get_tuple_value(data, LEX_NAME);
if (name == NULL) {
conf_die("Network with no name");
return 0;
}
n = hash_get(&bip->networks, name);
if (n) {
for (i = 0; i < n->serverc; i++)
free(n->serverv[i].host);
free(n->serverv);
n->serverv = NULL;
n->serverc = 0;
} else {
n = calloc(sizeof(struct network), 1);
hash_insert(&bip->networks, name, n);
}
2005-04-28 10:26:44 +02:00
while ((t = list_remove_first(data))) {
switch (t->type) {
case LEX_NAME:
MOVE_STRING(n->name, t->pdata);
2005-04-28 10:26:44 +02:00
break;
#ifdef HAVE_LIBSSL
case LEX_SSL:
n->ssl = t->ndata;
break;
#endif
case LEX_SERVER:
n->serverv = realloc(n->serverv, (n->serverc + 1)
* sizeof(struct server));
n->serverc++;
add_server(&n->serverv[n->serverc - 1], t->pdata);
2005-05-26 17:36:15 +02:00
free(t->pdata);
t->pdata = NULL;
2005-04-28 10:26:44 +02:00
break;
default:
2005-11-20 18:07:26 +01:00
conf_die("unknown keyword in network statement");
2005-05-26 17:36:15 +02:00
if (t->type == TUPLE_STR)
free(t->pdata);
2005-04-28 10:26:44 +02:00
break;
}
if (t->type == TUPLE_STR && t->pdata)
free(t->pdata);
2005-05-26 17:36:15 +02:00
free(t);
2005-04-28 10:26:44 +02:00
}
2005-05-26 17:36:15 +02:00
return 1;
2005-04-28 10:26:44 +02:00
}
static int add_connection(bip_t *bip, struct user *user, list_t *data)
2005-04-28 10:26:44 +02:00
{
struct tuple *t;
struct link *l;
struct chan_info *ci;
char *name = get_tuple_value(data, LEX_NAME);
2005-05-26 17:36:15 +02:00
if (name == NULL) {
conf_die("Connection with no name");
return 0;
2005-04-28 10:26:44 +02:00
}
l = hash_get(&user->connections, name);
if (!l) {
l = irc_link_new();
hash_insert(&user->connections, name, l);
list_add_last(&bip->link_list, l);
l->user = user;
l->log = log_new(user, name);
} else {
#warning "CODEME (user switch..)"
l->network = NULL;
2005-04-28 10:26:44 +02:00
}
while ((t = list_remove_first(data))) {
switch (t->type) {
case LEX_NAME:
MOVE_STRING(l->name, t->pdata);
2005-04-28 10:26:44 +02:00
break;
case LEX_NETWORK:
l->network = hash_get(&bip->networks, t->pdata);
if (!l->network) {
conf_die("Undefined network %s.\n",
2005-04-28 10:26:44 +02:00
t->pdata);
2005-05-26 17:36:15 +02:00
return 0;
}
2005-04-28 10:26:44 +02:00
break;
case LEX_NICK:
if (!is_valid_nick(t->pdata))
conf_die("Invalid nickname %s.", t->pdata);
MOVE_STRING(l->connect_nick, t->pdata);
2005-04-28 10:26:44 +02:00
break;
case LEX_USER:
MOVE_STRING(l->username, t->pdata);
2005-04-28 10:26:44 +02:00
break;
case LEX_REALNAME:
MOVE_STRING(l->realname, t->pdata);
2005-04-28 10:26:44 +02:00
break;
case LEX_PASSWORD:
MOVE_STRING(l->s_password, t->pdata);
2005-04-28 10:26:44 +02:00
break;
2005-11-20 18:07:26 +01:00
case LEX_VHOST:
MOVE_STRING(l->vhost, t->pdata);
2005-11-20 18:07:26 +01:00
break;
2005-04-28 10:26:44 +02:00
case LEX_CHANNEL:
ci = calloc(sizeof(struct chan_info), 1);
ci->name = get_tuple_value(t->pdata, LEX_NAME);
ci->key = get_tuple_value(t->pdata, LEX_KEY);
hash_insert(&l->chan_infos, ci->name, ci);
list_add_last(&l->chan_infos_order, ci);
2005-04-28 10:26:44 +02:00
break;
case LEX_FOLLOW_NICK:
l->follow_nick = t->ndata;
2005-04-28 10:26:44 +02:00
break;
case LEX_IGN_FIRST_NICK:
l->ignore_first_nick = t->ndata;
2005-04-28 10:26:44 +02:00
break;
case LEX_AWAY_NICK:
MOVE_STRING(l->away_nick, t->pdata);
2005-04-28 10:26:44 +02:00
break;
2006-09-25 14:08:31 +02:00
case LEX_NO_CLIENT_AWAY_MSG:
MOVE_STRING(l->no_client_away_msg, t->pdata);
2006-09-25 14:08:31 +02:00
break;
2005-04-28 10:26:44 +02:00
case LEX_ON_CONNECT_SEND:
list_add_last(&l->on_connect_send, t->pdata);
2005-04-28 10:26:44 +02:00
break;
default:
2005-11-20 18:07:26 +01:00
conf_die("unknown keyword in connection statement");
2005-05-26 17:36:15 +02:00
if (t->type == TUPLE_STR)
free(t->pdata);
2005-04-28 10:26:44 +02:00
}
if (t->type == TUPLE_STR && t->pdata)
free(t->pdata);
2005-05-26 17:36:15 +02:00
free(t);
2005-04-28 10:26:44 +02:00
}
/* checks that can only be here, or must */
if (!l->network)
2005-04-28 10:26:44 +02:00
conf_die("Missing network in connection block");
2005-05-26 17:36:15 +02:00
return 1;
2005-04-28 10:26:44 +02:00
}
static char *get_tuple_value(list_t *tuple_l, int lex)
2005-04-28 10:26:44 +02:00
{
struct tuple *t;
list_iterator_t it;
for (list_it_init(tuple_l, &it); (t = list_it_item(&it));
list_it_next(&it)) {
if (t->type == lex)
return t->pdata;
}
return NULL;
2005-04-28 10:26:44 +02:00
}
static int add_user(bip_t *bip, list_t *data)
2005-04-28 10:26:44 +02:00
{
2005-05-26 17:36:15 +02:00
int r;
2005-04-28 10:26:44 +02:00
struct tuple *t;
struct user *u;
2005-04-28 10:26:44 +02:00
char *name = get_tuple_value(data, LEX_NAME);
if (name == NULL) {
conf_die("User with no name");
return 0;
}
u = hash_get(&bip->users, name);
if (!u) {
u = calloc(sizeof(struct user), 1);
hash_insert(&bip->users, name, u);
hash_init(&u->connections, HASH_NOCASE);
} else {
FREE(u->name);
FREE(u->password);
FREE(u->default_nick);
FREE(u->default_username);
FREE(u->default_realname);
#ifdef HAVE_LIBSSL
FREE(u->ssl_check_store);
#endif
}
2005-04-28 10:26:44 +02:00
while ((t = list_remove_first(data))) {
switch (t->type) {
case LEX_NAME:
MOVE_STRING(u->name, t->pdata);
2005-04-28 10:26:44 +02:00
break;
case LEX_PASSWORD:
hash_binary(t->pdata, &u->password, &u->seed);
free(t->pdata);
break;
2005-07-09 14:55:01 +02:00
case LEX_DEFAULT_NICK:
MOVE_STRING(u->default_nick, t->pdata);
2005-07-09 14:55:01 +02:00
break;
case LEX_DEFAULT_USER:
MOVE_STRING(u->default_username, t->pdata);
2005-07-09 14:55:01 +02:00
break;
case LEX_DEFAULT_REALNAME:
MOVE_STRING(u->default_realname, t->pdata);
break;
case LEX_ALWAYS_BACKLOG:
u->always_backlog = t->ndata;
break;
case LEX_BACKLOG:
u->backlog = t->ndata;
break;
case LEX_BL_MSG_ONLY:
u->bl_msg_only = t->ndata;
2005-07-09 14:55:01 +02:00
break;
case LEX_BACKLOG_LINES:
u->backlog_lines = t->ndata;
break;
case LEX_BACKLOG_NO_TIMESTAMP:
u->backlog_no_timestamp = t->ndata;
break;
case LEX_BLRESET_ON_TALK:
u->blreset_on_talk = t->ndata;
break;
2005-04-28 10:26:44 +02:00
case LEX_CONNECTION:
r = add_connection(bip, u, t->pdata);
2005-05-26 17:36:15 +02:00
free(t->pdata);
if (!r)
return 0;
2005-04-28 10:26:44 +02:00
break;
#ifdef HAVE_LIBSSL
case LEX_SSL_CHECK_MODE:
if (!strncmp(t->pdata, "basic", 5))
u->ssl_check_mode = SSL_CHECK_BASIC;
if (!strncmp(t->pdata, "ca", 2))
u->ssl_check_mode = SSL_CHECK_CA;
free(t->pdata);
break;
case LEX_SSL_CHECK_STORE:
u->ssl_check_store = t->pdata;
break;
#endif
2005-04-28 10:26:44 +02:00
default:
2005-05-26 17:36:15 +02:00
conf_die("Uknown keyword in user statement");
2005-04-28 10:26:44 +02:00
break;
}
if (t->type == TUPLE_STR && t->pdata)
free(t->pdata);
2005-05-26 17:36:15 +02:00
free(t);
2005-04-28 10:26:44 +02:00
}
2005-05-26 17:36:15 +02:00
if (!u->password) {
2005-04-28 10:26:44 +02:00
conf_die("Missing password in user block");
2005-05-26 17:36:15 +02:00
return 0;
}
2005-04-28 10:26:44 +02:00
2005-05-26 17:36:15 +02:00
return 1;
2005-04-28 10:26:44 +02:00
}
static int validate_config(bip_t *bip)
{
/* nick username realname or default_{nick,username,realname} in user */
hash_iterator_t it, sit;
struct user *user;
struct link *link;
int r = 1;
for (hash_it_init(&bip->users, &it); (user = hash_it_item(&it));
hash_it_next(&it)) {
if (!user->default_nick || !user->default_username ||
!user->default_realname) {
for (hash_it_init(&user->connections, &sit);
(link = hash_it_item(&sit));
hash_it_next(&sit)) {
if ((!link->username &&
!user->default_username) ||
(!link->connect_nick &&
!user->default_nick) ||
(!link->realname &&
!user->default_realname))
link_kill(bip, link);
//conf_die("user: ... net: ... can i has nick/user/rael");
r = 0;
}
}
}
#warning CODE ME
#if 0
if (conf_backlog && !conf_log) {
if (conf_backlog_lines == 0) {
conf_die("You must set conf_backlog_lines if "
"conf_log = false and "
"conf_backlog = true");
}
}
#endif
return r;
}
int fireup(bip_t *bip, FILE *conf)
2005-04-28 10:26:44 +02:00
{
struct tuple *t;
list_t *l;
2005-05-26 17:36:15 +02:00
conf_start();
2005-04-28 10:26:44 +02:00
l = parse_conf(conf);
if (conf_error)
return 0;
2005-05-26 17:36:15 +02:00
while ((t = list_remove_first(l))) {
2005-04-28 10:26:44 +02:00
switch (t->type) {
case LEX_LOG_SYNC_INTERVAL:
conf_log_sync_interval = t->ndata;
break;
/*
2005-04-28 10:26:44 +02:00
case LEX_ALWAYS_BACKLOG:
conf_always_backlog = t->ndata;
break;
case LEX_BACKLOG:
conf_backlog = t->ndata;
break;
2006-10-21 14:06:31 +02:00
case LEX_BL_MSG_ONLY:
conf_bl_msg_only = t->ndata;
break;
2005-04-28 10:26:44 +02:00
case LEX_BACKLOG_LINES:
conf_backlog_lines = t->ndata;
break;
case LEX_BACKLOG_NO_TIMESTAMP:
conf_backlog_no_timestamp = t->ndata;
break;
case LEX_BLRESET_ON_TALK:
conf_blreset_on_talk = t->ndata;
break;
*/
case LEX_LOG:
conf_log = t->ndata;
break;
case LEX_LOG_SYSTEM:
conf_log_system = t->ndata;
break;
2005-04-28 10:26:44 +02:00
case LEX_LOG_ROOT:
MOVE_STRING(conf_log_root, t->pdata);
2005-04-28 10:26:44 +02:00
break;
case LEX_LOG_FORMAT:
MOVE_STRING(conf_log_format, t->pdata);
2005-04-28 10:26:44 +02:00
break;
case LEX_LOG_LEVEL:
conf_log_level = t->ndata;
break;
case LEX_IP:
MOVE_STRING(conf_ip, t->pdata);
2005-04-28 10:26:44 +02:00
break;
case LEX_PORT:
conf_port = t->ndata;
break;
case LEX_CSS:
conf_css = t->ndata;
break;
case LEX_PID_FILE:
MOVE_STRING(conf_pid_file, t->pdata);
break;
2005-04-28 10:26:44 +02:00
case LEX_NETWORK:
add_network(bip, t->pdata);
2005-05-26 17:36:15 +02:00
list_free(t->pdata);
2005-04-28 10:26:44 +02:00
break;
case LEX_USER:
add_user(bip, t->pdata);
2005-05-26 17:36:15 +02:00
list_free(t->pdata);
2005-04-28 10:26:44 +02:00
break;
default:
2005-05-26 17:36:15 +02:00
conf_die("Config error in base config (%d)", t->type);
2005-04-28 10:26:44 +02:00
}
if (t->type == TUPLE_STR && t->pdata)
free(t->pdata);
2005-05-26 17:36:15 +02:00
free(t);
2005-04-28 10:26:44 +02:00
}
2005-05-26 17:36:15 +02:00
free(root_list);
root_list = NULL;
2005-04-28 10:26:44 +02:00
validate_config(bip);
2005-04-28 10:26:44 +02:00
return 1;
}
#if 0
2007-05-26 12:31:44 +02:00
void ircize(bip_t *bip)
2005-04-28 10:26:44 +02:00
{
hash_iterator_t it;
for (hash_it_init(&conf_users, &it); hash_it_item(&it);
hash_it_next(&it)) {
struct c_user *u = hash_it_item(&it);
hash_t *adm_conn = hash_get(&adm_users, u->name);
if (!adm_conn) {
adm_conn = hash_new(HASH_NOCASE);
hash_insert(&adm_users, u->name, adm_conn);
mylog(LOG_DEBUG, "new user: \"%s\"", u->name);
} else {
mylog(LOG_DEBUG, "old user: \"%s\"", u->name);
}
2005-08-06 18:11:21 +02:00
/*
* TODO: keep track of removed connection on sighup
*/
2005-04-28 10:26:44 +02:00
/*
* A user has multiple connections.
* For each connections create a irc_client and a irc_server
* instance and register them in connection structure;
*/
list_iterator_t cit;
for (list_it_init(&u->connectionl, &cit); list_it_item(&cit);
list_it_next(&cit)) {
struct c_connection *c = list_it_item(&cit);
struct link *link;
int i;
if (!c->name)
fatal("no name for a connection");
link = hash_get(adm_conn, c->name);
if (!link) {
2007-02-08 23:53:48 +01:00
mylog(LOG_DEBUGVERB, "new connection: \"%s\"",
2005-04-28 10:26:44 +02:00
c->name);
link = irc_link_new();
hash_insert(adm_conn, c->name, link);
link->name = strmaydup(c->name);
link->log = log_new(u->name, link->name);
list_iterator_t chit;
for (list_it_init(&c->channell, &chit);
list_it_item(&chit);
list_it_next(&chit)) {
struct c_channel *chan =
list_it_item(&chit);
struct chan_info *ci = chan_info_new();
ci->name = strdup(chan->name);
ci->key = strmaydup(chan->key);
hash_insert(&link->chan_infos,
ci->name, ci);
2005-05-30 20:35:49 +02:00
list_add_last(&link->chan_infos_order,
ci);
2005-04-28 10:26:44 +02:00
}
2007-05-26 12:31:44 +02:00
list_add_last(&bip->link_list, link);
2005-04-28 10:26:44 +02:00
} else {
2007-02-08 23:53:48 +01:00
mylog(LOG_DEBUGVERB, "old connection: \"%s\"",
2005-04-28 10:26:44 +02:00
c->name);
MAYFREE(link->away_nick);
MAYFREE(link->password);
MAYFREE(link->user);
MAYFREE(link->real_name);
MAYFREE(link->s_password);
MAYFREE(link->connect_nick);
MAYFREE(link->vhost);
2005-08-25 10:17:10 +02:00
#ifdef HAVE_LIBSSL
MAYFREE(link->ssl_check_store);
sk_X509_free(link->untrusted_certs);
#endif
2005-11-20 18:07:26 +01:00
2005-04-28 10:26:44 +02:00
for (i = 0; i < link->serverc; i++)
server_free(link->serverv[i]);
free(link->serverv);
link->serverv = NULL;
link->serverc = 0;
}
link->follow_nick = c->follow_nick;
link->ignore_first_nick = c->ignore_first_nick;
2006-09-18 18:06:23 +02:00
char *s;
while ((s = list_remove_first(
&link->on_connect_send))) {
free(s);
}
list_append(&c->on_connect_send,
&link->on_connect_send);
2005-04-28 10:26:44 +02:00
link->away_nick = strmaydup(c->away_nick);
2006-09-18 20:05:17 +02:00
2006-09-18 18:06:23 +02:00
link->no_client_away_msg =
strmaydup(c->no_client_away_msg);
2005-04-28 10:26:44 +02:00
2005-07-09 14:55:01 +02:00
link->username = strmaydup(u->name);
2005-04-28 10:26:44 +02:00
link->password = malloc(20);
memcpy(link->password, u->password, 20);
link->seed = u->seed;
list_iterator_t seit;
for (list_it_init(&c->network->serverl, &seit);
list_it_item(&seit);
list_it_next(&seit)) {
struct server *s = list_it_item(&seit);
link->serverv = realloc(link->serverv,
(link->serverc + 1)
* sizeof(struct server *));
link->serverv[link->serverc] = server_new();
/* XXX: wrong */
link->serverv[link->serverc]->host
= strmaydup(s->host);
link->serverv[link->serverc]->port = s->port;
link->serverc++;
}
link->user = strmaydup(c->user);
2005-07-09 14:55:01 +02:00
if (!link->user)
link->user = strmaydup(u->default_user);
2005-04-28 10:26:44 +02:00
link->real_name = strmaydup(c->realname);
2005-07-09 14:55:01 +02:00
if (!link->real_name)
link->real_name =
strmaydup(u->default_realname);
2005-04-28 10:26:44 +02:00
link->s_password = strmaydup(c->password);
link->connect_nick = strmaydup(c->nick);
2005-07-09 14:55:01 +02:00
if (!link->connect_nick)
link->connect_nick = strmaydup(u->default_nick);
2005-04-28 10:26:44 +02:00
link->vhost = strmaydup(c->vhost);
link->bind_port = c->source_port;
#ifdef HAVE_LIBSSL
link->s_ssl = c->network->ssl;
link->ssl_check_mode = u->ssl_check_mode;
link->ssl_check_store = strmaydup(u->ssl_check_store);
2005-08-25 10:17:10 +02:00
link->untrusted_certs = sk_X509_new_null();
2005-04-28 10:26:44 +02:00
#endif
if (!link->user)
link->user = strmaydup("bip");
if (!link->connect_nick)
link->connect_nick = strmaydup("bip");
if (!link->real_name)
link->real_name = strmaydup("bip");
}
}
}
#endif
2005-04-28 10:26:44 +02:00
static void log_file_setup(void)
{
char buf[4096];
if (conf_log_system) {
if (conf_global_log_file && conf_global_log_file != stderr)
fclose(conf_global_log_file);
snprintf(buf, 4095, "%s/bip.log", conf_log_root);
FILE *f = fopen(buf, "a");
if (!f)
fatal("Can't open %s: %s", buf, strerror(errno));
conf_global_log_file = f;
} else {
conf_global_log_file = stderr;
}
}
2005-04-28 10:26:44 +02:00
int main(int argc, char **argv)
{
FILE *conf = NULL;
char *confpath = NULL;
int ch;
int r, fd;
2005-04-28 10:26:44 +02:00
char buf[30];
2007-05-26 12:31:44 +02:00
bip_t bip;
bip_init(&bip);
_bip = &bip;
2005-04-28 10:26:44 +02:00
conf_ip = strdup("0.0.0.0");
conf_port = 7778;
conf_css = 0;
signal(SIGPIPE, SIG_IGN);
signal(SIGHUP, reload_config);
signal(SIGINT, bad_quit);
signal(SIGQUIT, bad_quit);
signal(SIGTERM, bad_quit);
conf_log_root = NULL;
conf_log_format = NULL;
conf_log_level = LOG_INFO;
conf_backlog = 1;
conf_log = 1;
conf_log_system = 1;
2005-04-28 10:26:44 +02:00
conf_log_sync_interval = 5;
conf_daemonize = 1;
conf_global_log_file = stderr;
conf_pid_file = NULL;
2006-07-02 15:32:00 +02:00
while ((ch = getopt(argc, argv, "hnf:s:")) != -1) {
2005-04-28 10:26:44 +02:00
switch (ch) {
case 'f':
confpath = strdup(optarg);
break;
case 'n':
conf_daemonize = 0;
break;
2006-07-02 15:24:43 +02:00
case 's':
conf_biphome = strdup(optarg);
break;
2005-04-28 10:26:44 +02:00
default:
usage(argv[0]);
}
}
umask(0027);
2005-04-28 10:26:44 +02:00
if (confpath) {
conf = fopen(confpath, "r");
if (!conf)
fatal("config file not found");
}
if (!conf) {
char *home;
home = getenv("HOME");
if (!home)
fatal("no home");
confpath = malloc(strlen(home) + 1 + strlen(S_CONF) + 1);
*confpath = 0;
strcat(confpath, home);
strcat(confpath, "/");
strcat(confpath, S_CONF);
conf = fopen(confpath, "r");
if (!conf)
fatal("%s config file not found", confpath);
}
r = fireup(&bip, conf);
2005-04-28 10:26:44 +02:00
fclose(conf);
if (!r) {
fatal("%s", conf_errstr);
exit(28);
}
if (!conf_biphome) {
char *home = getenv("HOME");
if (!home) {
conf_die("no $HOME !, do you live in a trailer ?");
return 0;
}
conf_biphome = malloc(strlen(home) + strlen("/.bip") + 1);
strcpy(conf_biphome, home);
strcat(conf_biphome, "/.bip");
}
if (!conf_log_root) {
char *ap = "/logs";
conf_log_root = malloc(strlen(conf_biphome) + strlen(ap) + 1);
strcpy(conf_log_root, conf_biphome);
strcat(conf_log_root, ap);
mylog(LOG_INFO, "Default log root: %s", conf_log_root);
}
if (!conf_pid_file) {
char *pid = "/bip.pid";
conf_pid_file = malloc(strlen(conf_biphome) + strlen(pid) + 1);
strcpy(conf_pid_file, conf_biphome);
strcat(conf_pid_file, pid);
mylog(LOG_INFO, "Default pid file: %s", conf_pid_file);
}
#ifdef HAVE_LIBSSL
conf_ssl_certfile = NULL; /* Make into a config option */
if (!conf_ssl_certfile) {
char *ap = "/bip.pem";
if (conf_ssl_certfile) {
free(conf_ssl_certfile);
conf_ssl_certfile = NULL;
}
conf_ssl_certfile = malloc(strlen(conf_biphome) +
strlen(ap) + 1);
strcpy(conf_ssl_certfile, conf_biphome);
strcat(conf_ssl_certfile, ap);
mylog(LOG_INFO, "Default SSL certificate file: %s",
conf_ssl_certfile);
}
#endif
if (!conf_log_format)
conf_log_format = "%u/%n/%Y-%m/%c.%d.log";
2007-05-23 22:55:09 +02:00
check_dir(conf_log_root, 1);
2005-04-28 10:26:44 +02:00
fd = do_pid_stuff();
pid_t pid = 0;
log_file_setup();
2005-04-28 10:26:44 +02:00
if (conf_daemonize)
pid = daemonize();
else
pid = getpid();
snprintf(buf, 29, "%ld\n", (long unsigned int)pid);
write(fd, buf, strlen(buf));
close(fd);
2007-05-26 12:31:44 +02:00
bip.listener = listen_new(conf_ip, conf_port, conf_css);
if (!bip.listener)
2005-04-28 10:26:44 +02:00
fatal("Could not create listening socket");
for (;;) {
2005-07-09 14:55:01 +02:00
if (conf_error)
mylog(LOG_ERROR, "conf error: %s", conf_errstr);
2005-04-28 10:26:44 +02:00
2007-05-26 12:31:44 +02:00
irc_main(&bip);
2005-04-28 10:26:44 +02:00
sighup = 0;
conf = fopen(confpath, "r");
if (!conf)
fatal("%s config file not found", confpath);
fireup(&bip, conf);
2005-04-28 10:26:44 +02:00
fclose(conf);
/* re-open to allow logfile rotate */
log_file_setup();
2005-04-28 10:26:44 +02:00
}
return 1;
}
void write_user_list(connection_t *c, char *dest)
{
hash_iterator_t it;
hash_iterator_t lit;
2006-07-02 14:35:32 +02:00
char buf[4096];
2006-07-02 14:57:23 +02:00
2005-04-28 10:26:44 +02:00
WRITE_LINE2(c, P_IRCMASK, "PRIVMSG", dest, "bip user list:");
for (hash_it_init(&_bip->users, &it); hash_it_item(&it);
2005-04-28 10:26:44 +02:00
hash_it_next(&it)) {
struct user *u = hash_it_item(&it);
2005-04-28 10:26:44 +02:00
2006-07-02 14:35:32 +02:00
snprintf(buf, 4095, "* %s:", u->name);
2006-07-02 14:57:23 +02:00
buf[4095] = 0;
2006-07-02 14:35:32 +02:00
WRITE_LINE2(c, P_IRCMASK, "PRIVMSG", dest, buf);
for (hash_it_init(&u->connections, &lit); hash_it_item(&lit);
hash_it_next(&lit)) {
struct link *con = hash_it_item(&lit);
2006-07-02 14:35:32 +02:00
snprintf(buf, 4095, " - %s", con->name);
2006-07-02 14:57:23 +02:00
buf[4095] = 0;
2006-07-02 14:35:32 +02:00
WRITE_LINE2(c, P_IRCMASK, "PRIVMSG", dest, buf);
2005-04-28 10:26:44 +02:00
}
}
WRITE_LINE2(c, P_IRCMASK, "PRIVMSG", dest,
2006-07-02 14:35:32 +02:00
"End of bip user list");
2005-04-28 10:26:44 +02:00
}
2005-08-25 10:17:10 +02:00
#ifdef HAVE_LIBSSL
int link_add_untrusted(struct link_server *ls, X509 *cert)
{
int i;
/* Check whether the cert is already in the stack */
for (i = 0; i < sk_X509_num(LINK(ls)->untrusted_certs); i++) {
if (!X509_cmp(cert,
sk_X509_value(LINK(ls)->untrusted_certs, i)))
return 1;
}
return sk_X509_push(LINK(ls)->untrusted_certs, cert);
}
int ssl_check_trust(struct link_client *ic)
{
X509 *trustcert = NULL;
char subject[270];
char issuer[270];
unsigned char fp[EVP_MAX_MD_SIZE];
char fpstr[EVP_MAX_MD_SIZE * 3 + 20];
unsigned int fplen;
int i;
2005-08-25 10:17:10 +02:00
if(!LINK(ic)->untrusted_certs ||
sk_X509_num(LINK(ic)->untrusted_certs) <= 0)
return 0;
2006-09-25 14:08:31 +02:00
2005-08-25 10:17:10 +02:00
trustcert = sk_X509_value(LINK(ic)->untrusted_certs, 0);
strcpy(subject, "Subject: ");
strcpy(issuer, "Issuer: ");
strcpy(fpstr, "MD5 fingerprint: ");
X509_NAME_oneline(X509_get_subject_name(trustcert), subject + 9, 256);
X509_NAME_oneline(X509_get_issuer_name(trustcert), issuer + 9, 256);
X509_digest(trustcert, EVP_md5(), fp, &fplen);
for(i = 0; i < (int)fplen; i++)
sprintf(fpstr + 17 + (i * 3), "%02X%c",
fp[i], (i == (int)fplen - 1) ? '\0' : ':');
WRITE_LINE2(CONN(ic), P_SERV, "NOTICE", "TrustEm",
"This server SSL certificate was not "
"accepted because it is not in your store "
"of trusted certificates:");
2006-07-02 15:02:55 +02:00
2005-08-25 10:17:10 +02:00
WRITE_LINE2(CONN(ic), P_SERV, "NOTICE", "TrustEm", subject);
WRITE_LINE2(CONN(ic), P_SERV, "NOTICE", "TrustEm", issuer);
WRITE_LINE2(CONN(ic), P_SERV, "NOTICE", "TrustEm", fpstr);
WRITE_LINE2(CONN(ic), P_SERV, "NOTICE", "TrustEm",
"WARNING: if you've already trusted a "
"certificate for this server before, that "
"probably means it has changed.");
WRITE_LINE2(CONN(ic), P_SERV, "NOTICE", "TrustEm",
"If so, YOU MAY BE SUBJECT OF A "
"MAN-IN-THE-MIDDLE ATTACK! PLEASE DON'T TRUST "
"THIS CERTIFICATE IF YOU'RE NOT SURE THIS IS "
"NOT THE CASE.");
WRITE_LINE2(CONN(ic), P_SERV, "NOTICE", "TrustEm",
"Type /QUOTE BIP TRUST OK to trust this "
"certificate, /QUOTE BIP TRUST NO to discard it.");
2006-04-20 14:49:50 +02:00
2005-08-25 10:17:10 +02:00
return 1;
}
#if 0
static int ssl_trust_next_cert(struct link_client *ic)
{
(void)ic;
}
static int ssl_discard_next_cert(struct link_client *ic)
{
(void)ic;
}
#endif /* 0 */
#endif
#ifdef HAVE_LIBSSL
int adm_trust(struct link_client *ic, struct line *line)
2005-08-25 10:17:10 +02:00
{
if (ic->allow_trust != 1) {
mylog(LOG_ERROR, "User attempted TRUST command without "
"being allowed to!");
unbind_from_link(ic);
return OK_CLOSE;
}
if(!LINK(ic)->untrusted_certs ||
sk_X509_num(LINK(ic)->untrusted_certs) <= 0) {
2006-04-20 14:49:50 +02:00
/* shouldn't have been asked to /QUOTE BIP TRUST but well... */
2005-08-25 10:17:10 +02:00
WRITE_LINE2(CONN(ic), P_SERV, "NOTICE", "TrustEm",
"No untrusted certificates.");
return ERR_PROTOCOL;
}
if (line->elemc != 3)
return ERR_PROTOCOL;
if (!strcasecmp(line->elemv[2], "OK")) {
/* OK, attempt to trust the cert! */
BIO *bio = BIO_new_file(LINK(ic)->ssl_check_store, "a+");
X509 *trustcert = sk_X509_shift(LINK(ic)->untrusted_certs);
2005-08-25 10:17:10 +02:00
if(!bio || !trustcert ||
PEM_write_bio_X509(bio, trustcert) <= 0)
write_line_fast(CONN(ic), ":irc.bip.net NOTICE pouet "
":==== Error while trusting test!\r\n");
else
write_line_fast(CONN(ic), ":irc.bip.net NOTICE pouet "
":==== Certificate now trusted.\r\n");
BIO_free_all(bio);
X509_free(trustcert);
} else if (!strcasecmp(line->elemv[2], "NO")) {
/* NO, discard the cert! */
write_line_fast(CONN(ic), ":irc.bip.net NOTICE pouet "
":==== Certificate discarded.\r\n");
X509_free(sk_X509_shift(LINK(ic)->untrusted_certs));
} else
return ERR_PROTOCOL;
if (!ssl_check_trust(ic)) {
write_line_fast(CONN(ic), ":irc.bip.net NOTICE pouet "
":No more certificates waiting awaiting "
"user trust, thanks!\r\n");
write_line_fast(CONN(ic), ":irc.bip.net NOTICE pouet "
":If the certificate is trusted, bip should "
"be able to connect to the server on the "
"next retry. Please wait a while and try "
"connecting your client again.\r\n");
LINK(ic)->recon_timer = 1; /* Speed up reconnection... */
unbind_from_link(ic);
return OK_CLOSE;
}
return OK_FORGET;
}
#endif
2005-04-28 10:26:44 +02:00
extern struct link_client *reloading_client;
void adm_blreset(struct link_client *ic)
{
hash_iterator_t it;
for (hash_it_init(&LINK(ic)->log->logfgs, &it);
hash_it_item(&it);
hash_it_next(&it)) {
logfilegroup_t *lfg = hash_it_item(&it);
log_reset(lfg);
}
}
2005-04-28 10:26:44 +02:00
2006-07-02 13:50:20 +02:00
void adm_follow_nick(struct link_client *ic, char *val)
{
struct link *link = LINK(ic);
if (strncasecmp(val, "TRUE", 4) == 0) {
link->follow_nick = 1;
} else {
link->follow_nick = 0;
}
}
void adm_ignore_first_nick(struct link_client *ic, char *val)
{
struct link *link = LINK(ic);
if (strncasecmp(val, "TRUE", 4) == 0) {
link->ignore_first_nick = 1;
} else {
link->ignore_first_nick = 0;
}
}
void adm_on_connect_send(struct link_client *ic, char *val)
{
struct link *link = LINK(ic);
2006-09-18 20:05:17 +02:00
char *s;
2006-07-02 13:50:20 +02:00
if (val != NULL)
2006-09-18 20:05:17 +02:00
list_add_last(&link->on_connect_send, strdup(val));
else {
s = list_remove_last(&link->on_connect_send);
if (s)
free(s);
}
2006-07-02 13:50:20 +02:00
}
void adm_away_nick(struct link_client *ic, char *val)
{
struct link *link = LINK(ic);
if (link->away_nick) {
free(link->away_nick);
link->away_nick = NULL;
}
if (val != NULL)
link->away_nick = strdup(val);
}
int adm_bip(struct link_client *ic, struct line *line, unsigned int privmsg)
2005-04-28 10:26:44 +02:00
{
char *nick;
2005-04-28 10:26:44 +02:00
if (LINK(ic)->l_server)
nick = LINK(ic)->l_server->nick;
else
nick = LINK(ic)->prev_nick;
if (line->elemc < privmsg + 2)
2005-08-25 10:17:10 +02:00
return OK_FORGET;
2005-10-09 14:40:38 +02:00
if (strcasecmp(line->elemv[privmsg + 1], "RELOAD") == 0) {
2005-04-28 10:26:44 +02:00
reloading_client = ic;
sighup = 1;
} else if (strcasecmp(line->elemv[privmsg + 1], "LIST") == 0) {
2005-04-28 10:26:44 +02:00
write_user_list(CONN(ic), nick);
} else if (strcasecmp(line->elemv[privmsg + 1], "JUMP") == 0) {
2005-04-28 10:26:44 +02:00
if (LINK(ic)->l_server) {
WRITE_LINE1(CONN(LINK(ic)->l_server), NULL, "QUIT",
"jumpin' jumpin'");
connection_close(CONN(LINK(ic)->l_server));
}
} else if (strcasecmp(line->elemv[privmsg + 1], "BLRESET") == 0) {
adm_blreset(ic);
} else if (strcasecmp(line->elemv[privmsg + 1], "HELP") == 0) {
2005-04-28 10:26:44 +02:00
WRITE_LINE2(CONN(ic), P_IRCMASK, "PRIVMSG", nick,
2007-01-31 21:49:20 +01:00
"/BIP (RELOAD|LIST|JUMP|BLRESET|HELP|TRUST)");
WRITE_LINE2(CONN(ic), P_IRCMASK, "PRIVMSG", nick,
"/BIP FOLLOW_NICK|IGNORE_FIRST_NICK TRUE|FALSE");
WRITE_LINE2(CONN(ic), P_IRCMASK, "PRIVMSG", nick,
"/BIP ON_CONNECT_SEND <str> # Adds a string to "
"send on connect");
WRITE_LINE2(CONN(ic), P_IRCMASK, "PRIVMSG", nick,
"/BIP ON_CONNECT_SEND # Clears on_connect_send");
WRITE_LINE2(CONN(ic), P_IRCMASK, "PRIVMSG", nick,
"/BIP AWAY_NICK <nick> # Set away nick");
WRITE_LINE2(CONN(ic), P_IRCMASK, "PRIVMSG", nick,
"/BIP AWAY_NICK # clear away nick");
} else if (strcasecmp(line->elemv[privmsg + 1], "FOLLOW_NICK") == 0) {
if (line->elemc != privmsg + 3)
2006-07-02 13:50:20 +02:00
return OK_FORGET;
adm_follow_nick(ic, line->elemv[privmsg + 2]);
} else if (strcasecmp(line->elemv[privmsg + 1],
"IGNORE_FIRST_NICK") == 0) {
if (line->elemc != privmsg + 3)
2006-07-02 13:50:20 +02:00
return OK_FORGET;
adm_ignore_first_nick(ic, line->elemv[privmsg + 2]);
} else if (strcasecmp(line->elemv[privmsg + 1],
"ON_CONNECT_SEND") == 0) {
if (line->elemc == privmsg + 2)
2006-07-02 13:50:20 +02:00
adm_on_connect_send(ic, NULL);
else if (line->elemc == privmsg + 3)
adm_on_connect_send(ic, line->elemv[privmsg + 2]);
2006-07-02 13:50:20 +02:00
else
return OK_FORGET;
} else if (strcasecmp(line->elemv[privmsg + 1], "AWAY_NICK") == 0) {
if (line->elemc == privmsg + 2)
2006-07-02 13:50:20 +02:00
adm_away_nick(ic, NULL);
else if (line->elemc == privmsg + 3)
adm_away_nick(ic, line->elemv[privmsg + 2]);
2006-07-02 13:50:20 +02:00
else
return OK_FORGET;
2005-08-25 10:17:10 +02:00
#ifdef HAVE_LIBSSL
} else if (strcasecmp(line->elemv[privmsg + 1], "TRUST") == 0) {
2005-08-25 10:17:10 +02:00
return adm_trust(ic, line);
#endif
2005-04-28 10:26:44 +02:00
}
2005-08-25 10:17:10 +02:00
return OK_FORGET;
2005-04-28 10:26:44 +02:00
}
2005-05-26 17:36:15 +02:00
void free_conf(list_t *l)
{
struct tuple *t;
list_iterator_t li;
for (list_it_init(l, &li); (t = list_it_item(&li)); list_it_next(&li)) {
switch (t->tuple_type) {
case TUPLE_STR:
free(t->pdata); /* no break, for the style */
2005-10-09 14:40:38 +02:00
case TUPLE_INT:
2005-05-26 17:36:15 +02:00
free(t);
break;
case TUPLE_LIST:
free_conf(t->pdata);
break;
default:
fatal("internal error free_conf");
break;
}
}
}