move it to the github

This commit is contained in:
Mike Belopuhov 2010-01-08 12:20:28 +01:00
parent b47ffaa841
commit cd7b81d00d
8 changed files with 1668 additions and 0 deletions

10
Makefile Normal file
View File

@ -0,0 +1,10 @@
PROG= icbd
SRCS= cmd.c dns.c icb.c icbd.c
MAN= icbd.8
CFLAGS+=-W -Wall -Werror
DPADD= ${LIBEVENT}
LDADD= -levent
.include <bsd.prog.mk>

236
cmd.c Normal file
View File

@ -0,0 +1,236 @@
/*
* Copyright (c) 2009, 2010 Mike Belopuhov
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef lint
static const char rcsid[] = "$ABSD: cmd.c,v 1.21 2010/01/03 20:54:18 kmerz Exp $";
#endif /* not lint */
#include <sys/param.h>
#include <sys/queue.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <event.h>
#include "icb.h"
extern int creategroups;
void icb_cmd_boot(struct icb_session *, char *);
void icb_cmd_change(struct icb_session *, char *);
void icb_cmd_name(struct icb_session *, char *);
void icb_cmd_personal(struct icb_session *, char *);
void icb_cmd_pass(struct icb_session *, char *);
void icb_cmd_topic(struct icb_session *, char *);
void icb_cmd_who(struct icb_session *, char *);
void *
icb_cmd_lookup(char *cmd)
{
struct {
const char *cmd;
void (*handler)(struct icb_session *, char *);
} cmdtab[] = {
{ "boot", icb_cmd_boot },
{ "g", icb_cmd_change },
{ "m", icb_cmd_personal },
{ "msg", icb_cmd_personal },
{ "name", icb_cmd_name },
{ "pass", icb_cmd_pass },
{ "topic", icb_cmd_topic },
{ "w", icb_cmd_who },
{ NULL, NULL }
};
int i;
for (i = 0; cmdtab[i].cmd != NULL; i++)
if (strcasecmp(cmdtab[i].cmd, cmd) == 0)
return (cmdtab[i].handler);
return (NULL);
}
void
icb_cmd_boot(struct icb_session *is, char *arg)
{
struct icb_group *ig;
struct icb_session *s;
/* to boot or not to boot, that is the question */
ig = is->group;
if (!icb_ismoder(ig, is)) {
icb_status(is, STATUS_NOTIFY, "Sorry, booting is a privilege "
"you don't possess");
return;
}
/* who would be a target then? */
LIST_FOREACH(s, &ig->sess, entry) {
if (strcmp(s->nick, arg) == 0)
break;
}
if (s == NULL) {
icb_status(is, STATUS_NOTIFY, "No such user");
return;
}
/* okay, here we go, but first, be polite and notify a user */
icb_status(s, STATUS_BOOT, "%s booted you", is->nick);
icb_status_group(s->group, s, STATUS_BOOT, "%s was booted", s->nick);
icb_drop(s, "booted");
}
void
icb_cmd_change(struct icb_session *is, char *arg)
{
struct icb_group *ig;
struct icb_session *s;
int changing = 0;
if (strlen(arg) == 0) {
icb_error(is, "Invalid group");
return;
}
LIST_FOREACH(ig, &groups, entry) {
if (strcmp(ig->name, arg) == 0)
break;
}
if (ig == NULL) {
if (!creategroups) {
icb_error(is, "Invalid group");
return;
} else {
if ((ig = icb_addgroup(is, arg, NULL)) == NULL) {
icb_error(is, "Can't create group");
return;
}
icb_log(NULL, LOG_DEBUG, "%s created group %s",
is->nick, arg);
}
}
/* changing to the same group is strictly prohibited */
if (is->group && is->group == ig) {
icb_error(is, "Huh?");
return;
}
LIST_FOREACH(s, &ig->sess, entry) {
if (strcmp(s->nick, is->nick) == 0) {
icb_error(is, "Nick is already in use");
return;
}
}
if (is->group) {
changing = 1;
if (icb_ismoder(is->group, is))
(void)icb_pass(is->group, is, NULL);
LIST_REMOVE(is, entry);
icb_status_group(is->group, NULL, STATUS_DEPART,
"%s (%s@%s) just left", is->nick, is->client, is->host);
}
is->group = ig;
LIST_INSERT_HEAD(&ig->sess, is, entry);
/* notify group */
icb_status_group(ig, is, changing ? STATUS_ARRIVE : STATUS_SIGNON,
"%s (%s@%s) entered group", is->nick, is->client, is->host);
/* acknowledge successful join */
icb_status(is, STATUS_STATUS, "You are now in group %s%s", ig->name,
icb_ismoder(ig, is) ? " as moderator" : "");
/* send user a topic name */
if (strlen(ig->topic) > 0)
icb_status(is, STATUS_TOPIC, "The topic is: %s", ig->topic);
}
void
icb_cmd_name(struct icb_session *is, char *arg)
{
struct icb_group *ig = is->group;
struct icb_session *s;
if (strlen(arg) == 0) {
icb_status(is, STATUS_NAME, "Your nickname is %s",
is->nick);
return;
}
if (strcasecmp(arg, "admin") == 0) {
icb_error(is, "Wuff wuff!");
return;
}
/* sanitize user input */
if (strlen(arg) > ICB_MAXNICKLEN)
arg[ICB_MAXNICKLEN - 1] = '\0';
LIST_FOREACH(s, &ig->sess, entry) {
if (strcmp(s->nick, arg) == 0) {
icb_error(is, "Nick is already in use");
return;
}
}
icb_status_group(ig, NULL, STATUS_NAME,
"%s changed nickname to %s", is->nick, arg);
strlcpy(is->nick, arg, sizeof is->nick);
}
void
icb_cmd_personal(struct icb_session *is, char *arg)
{
char *p;
if ((p = strchr(arg, ' ')) == 0) {
icb_error(is, "Empty message");
return;
}
*p = '\0';
icb_privmsg(is, arg, ++p);
}
void
icb_cmd_pass(struct icb_session *is, char *arg __attribute__((unused)))
{
if (!icb_ismoder(is->group, is))
(void)icb_pass(is->group, is->group->moder, is);
}
void
icb_cmd_topic(struct icb_session *is, char *arg)
{
struct icb_group *ig = is->group;
if (strlen(arg) == 0) { /* querying the topic */
if (strlen(ig->topic) > 0)
icb_status(is, STATUS_TOPIC, "The topic is: %s",
ig->topic);
else
icb_status(is, STATUS_TOPIC, "The topic is not set.");
} else { /* setting the topic */
strlcpy(ig->topic, arg, sizeof ig->topic);
icb_status_group(ig, NULL, STATUS_TOPIC,
"%s changed the topic to \"%s\"", is->nick, ig->topic);
}
}
void
icb_cmd_who(struct icb_session *is, char *arg __attribute__((unused)))
{
icb_who(is, NULL);
}

158
dns.c Normal file
View File

@ -0,0 +1,158 @@
/*
* Copyright (c) 2009 Michael Shalayeff
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef lint
static const char rcsid[] = "$ABSD: dns.c,v 1.2 2010/01/03 01:30:00 kmerz Exp $";
#endif /* not lint */
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <sysexits.h>
#include <login_cap.h>
#include <event.h>
#include <pwd.h>
#include <netdb.h>
#include "icb.h"
#include "icbd.h"
void dns_dispatch(int, short, void *);
int dns_pipe;
int
icbd_dns_init(void)
{
struct event ev;
int pipe[2];
struct passwd *pw;
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe) == -1) {
syslog(LOG_ERR, "socketpair: %m");
exit(EX_OSERR);
}
switch (fork()) {
case -1:
syslog(LOG_ERR, "fork: %m");
exit(EX_OSERR);
case 0:
break;
default:
close(pipe[1]);
dns_pipe = pipe[0];
return (0);
}
setproctitle("dns resolver");
close(pipe[0]);
if ((pw = getpwnam(ICBD_USER)) == NULL) {
syslog(LOG_ERR, "No passwd entry for %s", ICBD_USER);
exit(EX_NOUSER);
}
if (setusercontext(NULL, pw, pw->pw_uid,
LOGIN_SETALL & ~LOGIN_SETUSER) < 0) {
syslog(LOG_ERR, "%s:%m", pw->pw_name);
exit(EX_NOPERM);
}
if (setuid(pw->pw_uid) < 0) {
syslog(LOG_ERR, "%d:%m", pw->pw_uid);
exit(EX_NOPERM);
}
if (chdir("/") < 0) {
syslog(LOG_ERR, "chdir: %m");
exit(EX_UNAVAILABLE);
}
event_init();
event_set(&ev, pipe[1], EV_READ | EV_PERSIST, dns_dispatch, NULL);
if (event_add(&ev, NULL) < 0) {
syslog(LOG_ERR, "event_add: %m");
exit (EX_UNAVAILABLE);
}
return event_dispatch();
}
void
dns_dispatch(int fd, short event, void *arg)
{
char host[NI_MAXHOST];
struct sockaddr_storage ss;
struct sockaddr *sa = (struct sockaddr *)&ss;
int gerr, ss_len = sizeof ss;
arg = NULL;
if (event != EV_READ)
return;
if (verbose)
syslog(LOG_DEBUG, "dns_dispatch");
if (read(fd, &ss, ss_len) != ss_len) {
syslog(LOG_ERR, "dns read: %m");
exit(1);
}
if ((gerr = getnameinfo(sa, sa->sa_len,
host, sizeof host, NULL, 0, NI_NOFQDN))) {
syslog(LOG_ERR, "getnameinfo: %s", gai_strerror(gerr));
write(fd, host, sizeof host);
return;
}
if (verbose)
syslog(LOG_DEBUG, "dns_dispatch: resolved %s", host);
if (write(fd, host, sizeof host) != sizeof host)
syslog(LOG_ERR, "dns write: %m");
}
int
dns_rresolv(struct icb_session *is, struct sockaddr_storage *ss)
{
/* one-shot event for the reply */
event_set(&is->ev, dns_pipe, EV_READ, icbd_dns, is);
if (event_add(&is->ev, NULL) < 0) {
syslog(LOG_ERR, "event_add: %m");
exit (EX_UNAVAILABLE);
}
inet_ntop(ss->ss_family, ss->ss_family == AF_INET ?
(void *)&((struct sockaddr_in *)ss)->sin_addr :
(void *)&((struct sockaddr_in6 *)ss)->sin6_addr,
is->host, sizeof is->host);
if (verbose)
syslog(LOG_DEBUG, "resolving: %s", is->host);
if (write(dns_pipe, ss, sizeof *ss) != sizeof *ss) {
syslog(LOG_ERR, "write: %m");
exit (EX_OSERR);
}
return 0;
}

524
icb.c Normal file
View File

@ -0,0 +1,524 @@
/*
* Copyright (c) 2009, 2010 Mike Belopuhov
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef lint
static const char rcsid[] = "$ABSD: icb.c,v 1.23 2010/01/03 20:54:18 kmerz Exp $";
#endif /* not lint */
#include <sys/param.h>
#include <sys/queue.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <event.h>
#include "icb.h"
#include "icbd.h"
extern int creategroups;
void icb_command(struct icb_session *, char *, char *);
void icb_groupmsg(struct icb_session *, char *);
void icb_login(struct icb_session *, char *, char *, char *);
char *icb_nextfield(char **);
/*
* icb_init: initializes pointers to callbacks
*/
void
icb_init(struct icbd_callbacks *ic)
{
icb_drop = ic->drop;
icb_log = ic->log;
icb_send = ic->send;
LIST_INIT(&groups);
}
/*
* icb_start: called upon accepting a new connection, greets new client
*/
void
icb_start(struct icb_session *is)
{
char hname[MAXHOSTNAMELEN];
bzero(hname, sizeof hname);
(void)gethostname(hname, sizeof hname);
icb_sendfmt(is, "%c%c%c%s%c%s", ICB_M_PROTO, '1', ICB_M_SEP, hname,
ICB_M_SEP, "icbd");
SETF(is->flags, ICB_SF_PROTOSENT);
}
/*
* icb_input: main input processing routine
*/
void
icb_input(struct icb_session *is)
{
char *msg = is->buffer;
char type;
is->last = getmonotime();
type = msg[1];
msg += 2;
if (!ISSETF(is->flags, ICB_SF_LOGGEDIN) && type != ICB_M_LOGIN) {
icb_error(is, "Not logged in");
return;
}
switch (type) {
case ICB_M_LOGIN: {
char *nick, *group, *client, *cmd;
client = icb_nextfield(&msg);
nick = icb_nextfield(&msg);
group = icb_nextfield(&msg);
cmd = icb_nextfield(&msg);
if (strlen(cmd) > 0 && cmd[0] == 'w') {
icb_error(is, "Command not implemented");
icb_drop(is, NULL);
return;
}
if (strlen(cmd) == 0 || strcmp(cmd, "login") != 0)
goto inputerr;
icb_login(is, group, nick, client);
break;
}
case ICB_M_OPEN: {
char *grpmsg;
grpmsg = icb_nextfield(&msg);
icb_groupmsg(is, grpmsg);
break;
}
case ICB_M_COMMAND: {
char *cmd, *arg;
cmd = icb_nextfield(&msg);
arg = icb_nextfield(&msg);
icb_command(is, cmd, arg);
break;
}
case ICB_M_PROTO:
case ICB_M_NOOP:
/* ignore */
break;
default:
/* everything else is not valid */
icb_error(is, "Bummer. This is a bummer, man.");
}
return;
inputerr:
icb_error(is, "Malformed packet");
}
/*
* icb_login: handles login ('a') packets
*/
void
icb_login(struct icb_session *is, char *group, char *nick, char *client)
{
char *defgrp = "1";
struct icb_group *ig;
struct icb_session *s;
if (!nick || strlen(nick) == 0) {
icb_error(is, "Invalid nick");
icb_drop(is, NULL);
return;
}
if (!group || strlen(group) == 0)
group = defgrp;
LIST_FOREACH(ig, &groups, entry) {
if (strcmp(ig->name, group) == 0)
break;
}
if (ig == NULL) {
if (!creategroups) {
icb_error(is, "Invalid group %s", group);
icb_drop(is, NULL);
return;
} else {
if ((ig = icb_addgroup(is, group, NULL)) == NULL) {
icb_error(is, "Can't create group %s", group);
return;
}
icb_log(NULL, LOG_DEBUG, "%s created group %s",
nick, group);
}
}
LIST_FOREACH(s, &ig->sess, entry) {
if (strcmp(s->nick, nick) == 0) {
icb_error(is, "Nick is already in use");
icb_drop(is, NULL);
return;
}
}
if (client && strlen(client) > 0)
strlcpy(is->client, client, sizeof is->client);
strlcpy(is->nick, nick, sizeof is->nick);
is->group = ig;
is->login = time(NULL);
is->last = getmonotime();
/* notify group */
icb_status_group(ig, NULL, STATUS_SIGNON, "%s (%s@%s) entered group",
is->nick, is->client, is->host);
CLRF(is->flags, ICB_SF_PROTOSENT);
SETF(is->flags, ICB_SF_LOGGEDIN);
LIST_INSERT_HEAD(&ig->sess, is, entry);
/* acknowledge successful login */
icb_sendfmt(is, "%c", ICB_M_LOGIN);
/* notify user */
icb_status(is, STATUS_STATUS, "You are now in group %s%s", ig->name,
icb_ismoder(ig, is) ? " as moderator" : "");
/* send user a topic name */
if (strlen(ig->topic) > 0)
icb_status(is, STATUS_TOPIC, "Topic for %s is \"%s\"",
ig->name, ig->topic);
}
/*
* icb_groupmsg: handles open message ('b') packets
*/
void
icb_groupmsg(struct icb_session *is, char *msg)
{
char buf[ICB_MSGSIZE];
struct icb_group *ig = is->group;
struct icb_session *s;
int buflen = 1;
if (strlen(msg) == 0) {
icb_error(is, "Empty message");
return;
}
buflen += snprintf(&buf[1], sizeof buf - 1, "%c%s%c%s", ICB_M_OPEN,
is->nick, ICB_M_SEP, msg);
buf[0] = buflen;
LIST_FOREACH(s, &ig->sess, entry) {
if (s == is)
continue;
icb_send(s, buf, buflen + 1);
}
}
/*
* icb_privmsg: handles personal message ('c') packets
*/
void
icb_privmsg(struct icb_session *is, char *whom, char *msg)
{
struct icb_group *ig = is->group;
struct icb_session *s;
LIST_FOREACH(s, &ig->sess, entry) {
if (strcmp(s->nick, whom) == 0)
break;
}
if (!s) {
icb_error(is, "No such user %s", whom);
return;
}
icb_sendfmt(s, "%c%s%c%s", ICB_M_PERSONAL, is->nick, ICB_M_SEP, msg);
}
/*
* icb_command: handles command ('h') packets
*/
void
icb_command(struct icb_session *is, char *cmd, char *arg)
{
void (*handler)(struct icb_session *, char *);
if ((handler = icb_cmd_lookup(cmd)) == NULL) {
icb_error(is, "Unsupported command: %s", cmd);
return;
}
handler(is, arg);
}
/*
* icb_cmdout: sends out command output ('i') packets, called from the
* command handlers
*/
void
icb_cmdout(struct icb_session *is, int type, char *outmsg)
{
char *otype = NULL;
switch (type) {
case CMDOUT_CO:
otype = "co";
break;
case CMDOUT_EC:
otype = "ec";
break;
case CMDOUT_WL:
otype = "wl";
break;
case CMDOUT_WG:
otype = "wg";
break;
default:
icb_log(is, LOG_ERR, "unknown cmdout type");
return;
}
icb_sendfmt(is, "%c%s%c%s", ICB_M_CMDOUT, otype, ICB_M_SEP, outmsg);
}
/*
* icb_status: sends a status message ('d') to the client
*/
void
icb_status(struct icb_session *is, int type, const char *fmt, ...)
{
va_list ap;
char buf[ICB_MSGSIZE];
int buflen = 1;
static const struct {
int type;
const char *msg;
} msgtab[] = {
{ STATUS_ARRIVE, "Arrive" },
{ STATUS_BOOT, "Boot" },
{ STATUS_DEPART, "Depart" },
{ STATUS_NAME, "Name" },
{ STATUS_NOTIFY, "Notify" },
{ STATUS_SIGNON, "Sign-on" },
{ STATUS_SIGNOFF, "Sign-off" },
{ STATUS_STATUS, "Status" },
{ STATUS_TOPIC, "Topic" },
{ STATUS_WARNING, "Warning" },
{ NULL, NULL }
};
if (type < 0 || type > (int)nitems(msgtab) - 1)
return;
va_start(ap, fmt);
buflen += snprintf(&buf[1], sizeof buf - 1, "%c%s%c", ICB_M_STATUS,
msgtab[type].msg, ICB_M_SEP);
buflen += vsnprintf(&buf[buflen], sizeof buf - buflen, fmt, ap);
buf[0] = buflen;
va_end(ap);
icb_send(is, buf, buflen + 1);
}
/*
* icb_status: sends a status message ('d') to the group except of the
* "ex" if it's not NULL
*/
void
icb_status_group(struct icb_group *ig, struct icb_session *ex, int type,
const char *fmt, ...)
{
char buf[ICB_MSGSIZE];
va_list ap;
struct icb_session *s;
va_start(ap, fmt);
(void)vsnprintf(buf, sizeof buf, fmt, ap);
LIST_FOREACH(s, &ig->sess, entry) {
if (ex && s == ex)
continue;
icb_status(s, type, buf);
}
icb_log(NULL, LOG_DEBUG, "%s", buf);
va_end(ap);
}
/*
* icb_error: sends an error message ('e') to the client
*/
void
icb_error(struct icb_session *is, const char *fmt, ...)
{
char buf[ICB_MSGSIZE];
va_list ap;
int buflen = 1;
va_start(ap, fmt);
buflen += vsnprintf(&buf[2], sizeof buf - 2, fmt, ap);
va_end(ap);
buf[0] = ++buflen; /* account for ICB_M_ERROR */
buf[1] = ICB_M_ERROR;
icb_send(is, buf, buflen + 1);
icb_log(is, LOG_DEBUG, "%s", buf + 2);
}
/*
* icb_remove: removes a session from the associated group
*/
void
icb_remove(struct icb_session *is, char *reason)
{
if (is->group) {
if (icb_ismoder(is->group, is))
(void)icb_pass(is->group, is, NULL);
LIST_REMOVE(is, entry);
if (reason)
icb_status_group(is->group, NULL, STATUS_SIGNOFF,
"%s (%s@%s) just left: %s", is->nick, is->client,
is->host, reason);
else
icb_status_group(is->group, NULL, STATUS_SIGNOFF,
"%s (%s@%s) just left", is->nick, is->client,
is->host);
}
}
/*
* icb_addgroup: adds a new group to the list
*/
struct icb_group *
icb_addgroup(struct icb_session *is, char *name, char *mpass)
{
struct icb_group *ig;
if ((ig = calloc(1, sizeof *ig)) == NULL)
return (NULL);
strlcpy(ig->name, name, sizeof ig->name);
if (mpass)
strlcpy(ig->mpass, mpass, sizeof ig->mpass);
if (is)
ig->moder = is;
LIST_INIT(&ig->sess);
LIST_INSERT_HEAD(&groups, ig, entry);
return (ig);
}
#ifdef notused
/*
* icb_delgroup: removes a group from the list
*/
void
icb_delgroup(struct icb_group *ig)
{
struct icb_session *s;
/* well, i guess we should kick out participants! ;-) */
LIST_FOREACH(s, &ig->sess, entry) {
icb_status(s, STATUS_WARNING, "Group dismissed");
s->group = NULL;
}
LIST_REMOVE(ig, entry);
bzero(ig, sizeof ig); /* paranoic thing, obviously */
free(ig);
}
#endif
/*
* icb_who: sends a list of users of the specified group (or the current
* one otherwise) in the "wl" format
*/
void
icb_who(struct icb_session *is, struct icb_group *ig)
{
char buf[ICB_MSGSIZE];
struct icb_session *s;
if (!ig)
ig = is->group;
LIST_FOREACH(s, &ig->sess, entry) {
(void)snprintf(buf, sizeof buf,
"%c%c%s%c%d%c0%c%d%c%s%c%s%c%s",
icb_ismoder(ig, s) ? '*' : ' ', ICB_M_SEP,
s->nick, ICB_M_SEP, getmonotime() - s->last,
ICB_M_SEP, ICB_M_SEP, s->login, ICB_M_SEP,
s->client, ICB_M_SEP, s->host, ICB_M_SEP, " ");
icb_cmdout(is, CMDOUT_WL, buf);
}
}
/*
* icb_ismoder: checks whether group is moderated by "is"
*/
int
icb_ismoder(struct icb_group *ig, struct icb_session *is)
{
if (ig->moder && ig->moder == is)
return (1);
return (0);
}
/*
* icb_pass: passes moderation of group "ig" from "from" to "to",
* returns -1 if "from" is not a moderator, 1 if passed
* to "to" and 0 otherwise (no moderator or passed to the
* internal bot)
*/
int
icb_pass(struct icb_group *ig, struct icb_session *from,
struct icb_session *to)
{
if (ig->moder && ig->moder != from)
return (-1);
if (!from && !to)
return (-1);
ig->moder = to;
if (to)
icb_status(to, STATUS_NOTIFY, "%s just passed you moderation"
" of %s", from ? from->nick : "server", ig->name);
icb_status_group(ig, to, STATUS_NOTIFY, "%s has passed moderation "
"to %s", from ? from->nick : "server", to ? to->nick : "server");
return (1);
}
/*
* icb_nextfield: advances through a given buffer returning pointer to
* the beginning of the icb field or an empty string otherwise
*/
char *
icb_nextfield(char **buf)
{
char *start = *buf;
while (*buf && **buf != '\0' && **buf != ICB_M_SEP)
(*buf)++;
if (*buf && **buf == ICB_M_SEP) {
**buf = '\0';
(*buf)++;
}
return (start);
}
/*
* icb_sendfmt: formats a string and sends it over
*/
void
icb_sendfmt(struct icb_session *is, const char *fmt, ...)
{
char buf[ICB_MSGSIZE];
va_list ap;
int buflen = 1;
va_start(ap, fmt);
buflen += vsnprintf(&buf[1], sizeof buf - 1, fmt, ap);
va_end(ap);
buf[0] = buflen;
icb_send(is, buf, buflen + 1);
}

132
icb.h Normal file
View File

@ -0,0 +1,132 @@
/*
* Copyright (c) 2009 Mike Belopuhov
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/queue.h>
#define ICB_MSGSIZE 256
#define ICB_MAXGRPLEN 32
#define ICB_MAXNICKLEN 32
#define ICB_MAXPASSLEN 32
#define ICB_MAXTOPICLEN 160
#define ICB_M_LOGIN 'a'
#define ICB_M_OPEN 'b'
#define ICB_M_PERSONAL 'c'
#define ICB_M_STATUS 'd'
enum {
STATUS_ARRIVE,
STATUS_BOOT,
STATUS_DEPART,
STATUS_NAME,
STATUS_NOTIFY,
STATUS_SIGNON,
STATUS_SIGNOFF,
STATUS_STATUS,
STATUS_TOPIC,
STATUS_WARNING
};
#define ICB_M_ERROR 'e'
#define ICB_M_IMPORTANT 'f'
#define ICB_M_EXIT 'g'
#define ICB_M_COMMAND 'h'
#define ICB_M_CMDOUT 'i'
enum {
CMDOUT_CO,
CMDOUT_EC,
CMDOUT_WL,
CMDOUT_WG,
};
#define ICB_M_PROTO 'j'
#define ICB_M_BEEP 'k'
#define ICB_M_PING 'l'
#define ICB_M_PONG 'm'
#define ICB_M_NOOP 'n'
#define ICB_M_SEP '\001'
struct icb_group;
struct icb_session {
char nick[ICB_MAXNICKLEN];
char client[ICB_MAXNICKLEN];
char host[MAXHOSTNAMELEN];
char buffer[ICB_MSGSIZE+1];
struct event ev;
LIST_ENTRY(icb_session) entry;
struct bufferevent *bev;
struct icb_group *group;
size_t length;
time_t login;
time_t last;
int port;
uint32_t flags;
#define SETF(t, f) ((t) |= (f))
#define CLRF(t, f) ((t) &= ~(f))
#define ISSETF(t, f) ((t) & (f))
#define ICB_SF_UNKNOWN 0x00
#define ICB_SF_PROTOSENT 0x01
#define ICB_SF_LOGGEDIN 0x02
#define ICB_SF_NOGROUP 0x08
#define ICB_SF_MODERATOR 0x10
};
struct icb_group {
char name[ICB_MAXGRPLEN];
char mpass[ICB_MAXPASSLEN];
char topic[ICB_MAXTOPICLEN];
LIST_ENTRY(icb_group) entry;
LIST_HEAD(, icb_session) sess;
struct icb_session *moder;
};
LIST_HEAD(icb_grlist, icb_group) groups;
struct icbd_callbacks {
void (*drop)(struct icb_session *, char *);
void (*log)(struct icb_session *, int, const char *, ...);
void (*send)(struct icb_session *, char *, ssize_t);
};
#ifndef nitems
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#endif
/* cmd.c */
void *icb_cmd_lookup(char *);
/* icb.c */
struct icb_group *icb_addgroup(struct icb_session *, char *, char *);
void icb_cmdout(struct icb_session *, int, char *);
void icb_delgroup(struct icb_group *);
void icb_error(struct icb_session *, const char *, ...);
void icb_init(struct icbd_callbacks *);
void icb_input(struct icb_session *);
int icb_ismoder(struct icb_group *, struct icb_session *);
int icb_pass(struct icb_group *, struct icb_session *, struct icb_session *);
void icb_privmsg(struct icb_session *, char *, char *);
void icb_remove(struct icb_session *, char *);
void icb_sendfmt(struct icb_session *, const char *, ...);
void icb_start(struct icb_session *);
void icb_status(struct icb_session *, int, const char *, ...);
void icb_status_group(struct icb_group *, struct icb_session *, int ,
const char *, ...);
void icb_who(struct icb_session *, struct icb_group *);
/* callbacks from icbd.c */
void (*icb_drop)(struct icb_session *, char *);
void (*icb_log)(struct icb_session *, int, const char *, ...);
void (*icb_send)(struct icb_session *, char *, ssize_t);

84
icbd.8 Normal file
View File

@ -0,0 +1,84 @@
.\"
.\" Copyright (c) 2009 Mike Belopuhov
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: April 27 2009 $
.Dt ICBD 8
.Os
.Sh NAME
.Nm icbd
.Nd "Internet Citizen's Band protocol daemon"
.Sh SYNOPSIS
.Nm icbd
.Bk -words
.Op Fl 46Cdv
.Oo Fl G Ar group1
.Ns Oo , Ns Ar ... Oc Oc
.Sm off
.Oo Ar addr
.Ns Op : Ns Ar port
.Ns Oo \ Ns Ar ... Oc Oc
.Sm on
.Ek
.Sh DESCRIPTION
.Nm
is a Internet Citizen's Band
.Pq ICB
protocol daemon which allows peoples to chat online.
.Pp
Please refer to the document specified in the
.Sx SEE ALSO
section for more information about ICB protocol.
.Pp
The options are as follows:
.Bl -tag -width "-G group"
.It Fl 4
Bind to IPv4 addresses only.
.It Fl 6
Bind to IPv6 addresses only.
.It Fl C
Allow users to create groups.
Please note, that everyone creating a new group becomes its moderator.
.It Fl d
Do not daemonize.
If this option is specified,
.Nm
will run in the foreground and log to
.Em stderr .
.It Fl G Xo
.Ar group1 Ns Op , Ns Ar ...
.Xc
Instruct
.Nm
to create specified groups at startup.
.It Fl v
Produce more verbose output.
.El
.Pp
A list of addresses and/or ports to bind to is specified after all arguments.
By default,
.Nm
will try to listen on all interfaces using
.Em icb
protocol port number specified in
.Em /etc/services .
.Sh SEE ALSO
.Rs
.%R ftp://ftp.icb.net/pub/icb/src/icbd/Protocol.html
.%T ICB Protocol
.Re
.Sh AUTHORS
The
.Nm
program was written by Mike Belopuhov.

492
icbd.c Normal file
View File

@ -0,0 +1,492 @@
/*
* Copyright (c) 2009 Mike Belopuhov
* Copyright (c) 2007 Oleg Safiullin
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef lint
static const char rcsid[] = "$ABSD: icbd.c,v 1.22 2010/01/03 20:54:18 kmerz Exp $";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sysexits.h>
#include <syslog.h>
#include <pwd.h>
#include <login_cap.h>
#include <locale.h>
#include <netdb.h>
#include <event.h>
#include <errno.h>
#include <err.h>
#include "icb.h"
#include "icbd.h"
extern char *__progname;
int creategroups;
int foreground;
int verbose;
void usage(void);
void getpeerinfo(struct icb_session *);
void icbd_accept(int, short, void *);
void icbd_drop(struct icb_session *, char *);
void icbd_ioerr(struct bufferevent *, short, void *);
void icbd_dispatch(struct bufferevent *, void *);
void icbd_log(struct icb_session *, int, const char *, ...);
void icbd_grplist(char *);
void icbd_restrict(void);
void icbd_write(struct icb_session *, char *, ssize_t);
int
main(int argc, char *argv[])
{
struct icbd_callbacks ic = { icbd_drop, icbd_log, icbd_write };
const char *cause = NULL;
int ch, nsocks = 0, save_errno = 0;
int inet4 = 0, inet6 = 0;
/* init group lists before calling icb_addgroup */
icb_init(&ic);
while ((ch = getopt(argc, argv, "46CdG:v")) != -1)
switch (ch) {
case '4':
inet4++;
break;
case '6':
inet6++;
break;
case 'C':
creategroups++;
break;
case 'd':
foreground++;
break;
case 'G':
icbd_grplist(optarg);
break;
case 'v':
verbose++;
break;
default:
usage();
/* NOTREACHED */
}
argc -= optind;
argv += optind;
/* add group "1" as it's a login group for most of the clients */
if (icb_addgroup(NULL, "1", NULL) == NULL)
err(EX_UNAVAILABLE, NULL);
if (argc == 0)
argc++;
if (inet4 && inet6)
errx(EX_CONFIG, "Can't specify both -4 and -6");
tzset();
(void)setlocale(LC_ALL, "C");
if (foreground)
openlog("icbd", LOG_PID | LOG_PERROR, LOG_DAEMON);
else
openlog("icbd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
if (!foreground && daemon(0, 0) < 0)
err(EX_OSERR, NULL);
(void)event_init();
for (ch = 0; ch < argc; ch++) {
struct addrinfo hints, *res, *res0;
struct event *ev;
char *addr, *port;
int error, s, on = 1;
addr = port = NULL;
if (argv[ch] != NULL) {
if (argv[ch][0] != ':')
addr = argv[ch];
if ((port = strrchr(argv[ch], ':')) != NULL)
*port++ = '\0';
}
bzero(&hints, sizeof hints);
if (inet4 || inet6)
hints.ai_family = inet4 ? PF_INET : PF_INET6;
else
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if ((error = getaddrinfo(addr, port ? port : "icb", &hints,
&res0)) != 0) {
syslog(LOG_ERR, "%s", gai_strerror(error));
return (EX_UNAVAILABLE);
}
for (res = res0; res != NULL; res = res->ai_next) {
if ((s = socket(res->ai_family, res->ai_socktype,
res->ai_protocol)) < 0) {
cause = "socket";
save_errno = errno;
continue;
}
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on,
sizeof on) < 0) {
cause = "SO_REUSEADDR";
save_errno = errno;
(void)close(s);
continue;
}
if (bind(s, res->ai_addr, res->ai_addrlen) < 0) {
cause = "bind";
save_errno = errno;
(void)close(s);
continue;
}
(void)listen(s, TCP_BACKLOG);
if ((ev = calloc(1, sizeof *ev)) == NULL)
err(EX_UNAVAILABLE, NULL);
event_set(ev, s, EV_READ | EV_PERSIST, icbd_accept, ev);
if (event_add(ev, NULL) < 0) {
syslog(LOG_ERR, "event_add: %m");
return (EX_UNAVAILABLE);
}
nsocks++;
}
freeaddrinfo(res0);
}
if (nsocks == 0) {
errno = save_errno;
syslog(LOG_ERR, "%s: %m", cause);
return (EX_UNAVAILABLE);
}
/* start a dns resolver thread */
icbd_dns_init();
if (!foreground)
icbd_restrict();
(void)signal(SIGPIPE, SIG_IGN);
(void)event_dispatch();
syslog(LOG_ERR, "event_dispatch: %m");
return (EX_UNAVAILABLE);
}
void
icbd_dns(int fd, short event, void *arg)
{
struct icb_session *is = arg;
if (event != EV_READ)
return;
if (verbose)
syslog(LOG_DEBUG, "icbd_dns");
if (read(fd, is->host, sizeof is->host) < 0)
syslog(LOG_ERR, "read: %m");
is->host[sizeof is->host - 1] = '\0';
if (verbose)
syslog(LOG_DEBUG, "icbd_dns: resolved %s", is->host);
}
void
icbd_accept(int fd, short event __attribute__((__unused__)),
void *arg __attribute__((__unused__)))
{
struct sockaddr_storage ss;
struct icb_session *is;
socklen_t ss_len = sizeof ss;
int s;
ss.ss_len = ss_len;
if ((s = accept(fd, (struct sockaddr *)&ss, &ss_len)) < 0) {
syslog(LOG_ERR, "accept: %m");
return;
}
if ((is = calloc(1, sizeof *is)) == NULL) {
syslog(LOG_ERR, "calloc: %m");
(void)close(s);
return;
}
if ((is->bev = bufferevent_new(s, icbd_dispatch, NULL, icbd_ioerr,
is)) == NULL) {
syslog(LOG_ERR, "bufferevent_new: %m");
(void)close(s);
free(is);
return;
}
if (bufferevent_enable(is->bev, EV_READ)) {
syslog(LOG_ERR, "bufferevent_enable: %m");
(void)close(s);
bufferevent_free(is->bev);
free(is);
return;
}
/* save host information */
getpeerinfo(is);
/* start icb conversation */
icb_start(is);
}
__dead void
usage(void)
{
(void)fprintf(stderr, "usage: %s [-46Cdv] [-G group1[,group2,...]] "
"[[addr][:port] ...]\n", __progname);
exit(EX_USAGE);
}
/*
* bufferevent functions
*/
void
icbd_dispatch(struct bufferevent *bev, void *arg)
{
struct icb_session *is = (struct icb_session *)arg;
if (is->length == 0) {
bzero(is->buffer, sizeof is->buffer);
/* read length */
(void)bufferevent_read(bev, is->buffer, 1);
/* we're about to read the whole packet */
is->length = (size_t)(unsigned char)is->buffer[0];
if (is->length == 0) {
icbd_drop(is, "invalid packet");
return;
}
if (EVBUFFER_LENGTH(EVBUFFER_INPUT(bev)) < is->length) {
/* set watermark to the expected length */
bufferevent_setwatermark(bev, EV_READ, is->length, 0);
return;
}
}
(void)bufferevent_read(bev, &is->buffer[1], is->length);
#ifdef DEBUG
{
int i;
printf("-> read from %s:%d:\n", is->host, is->port);
for (i = 0; i < (int)is->length + 1; i++)
printf(" %02x", (unsigned char)is->buffer[i]);
printf("\n");
}
#endif
icb_input(is);
is->length = 0;
}
void
icbd_write(struct icb_session *is, char *buf, ssize_t size)
{
if (bufferevent_write(is->bev, buf, size) == -1)
syslog(LOG_ERR, "bufferevent_write: %m");
#ifdef DEBUG
{
int i;
printf("-> wrote to %s:%d:\n", is->host, is->port);
for (i = 0; i < size; i++)
printf(" %02x", (unsigned char)buf[i]);
printf("\n");
}
#endif
}
void
icbd_drop(struct icb_session *is, char *reason)
{
if (reason) {
icb_remove(is, reason);
icbd_log(is, LOG_DEBUG, reason);
} else
icb_remove(is, NULL);
(void)evbuffer_write(EVBUFFER_OUTPUT(is->bev), EVBUFFER_FD(is->bev));
(void)close(EVBUFFER_FD(is->bev));
bufferevent_free(is->bev);
free(is);
}
void
icbd_ioerr(struct bufferevent *bev __attribute__((__unused__)), short what,
void *arg)
{
struct icb_session *is = (struct icb_session *)arg;
if (what & EVBUFFER_TIMEOUT)
icbd_drop(is, "timeout");
else if (what & EVBUFFER_EOF)
icbd_drop(is, NULL);
else if (what & EVBUFFER_ERROR)
icbd_drop(is, (what & EVBUFFER_READ) ? "read error" :
"write error");
}
void
icbd_log(struct icb_session *is, int level, const char *fmt, ...)
{
char buf[512];
va_list ap;
if (!verbose && level == LOG_DEBUG)
return;
va_start(ap, fmt);
(void)vsnprintf(buf, sizeof buf, fmt, ap);
va_end(ap);
if (is)
syslog(level, "%s:%u: %s", is->host, is->port, buf);
else
syslog(level, "%s", buf);
}
void
icbd_restrict(void)
{
struct stat sb;
struct passwd *pw;
if ((pw = getpwnam(ICBD_USER)) == NULL) {
syslog(LOG_ERR, "No passwd entry for %s", ICBD_USER);
exit(EX_NOUSER);
}
if (setusercontext(NULL, pw, pw->pw_uid,
LOGIN_SETALL & ~LOGIN_SETUSER) < 0) {
syslog(LOG_ERR, "%s: %m", pw->pw_name);
exit(EX_NOPERM);
}
if (stat(pw->pw_dir, &sb) == -1) {
syslog(LOG_ERR, "%s: %m", pw->pw_name);
exit(EX_NOPERM);
}
if (sb.st_uid != 0 || (sb.st_mode & (S_IWGRP|S_IWOTH)) != 0) {
syslog(LOG_ERR, "bad directory permissions");
exit(EX_NOPERM);
}
if (chroot(pw->pw_dir) < 0) {
syslog(LOG_ERR, "%s: %m", pw->pw_dir);
exit(EX_UNAVAILABLE);
}
if (setuid(pw->pw_uid) < 0) {
syslog(LOG_ERR, "%d: %m", pw->pw_uid);
exit(EX_NOPERM);
}
if (chdir("/") < 0) {
syslog(LOG_ERR, "/: %m");
exit(EX_UNAVAILABLE);
}
(void)setproctitle("icbd");
}
void
icbd_grplist(char *list)
{
char *s, *s1, *s2;
int last = 0;
if (!list || strlen(list) == 0)
return;
/* "group1[:pass1][,group2[:pass2],...]" */
s = list;
s1 = s2 = NULL;
while (!last && s) {
if ((s1 = strchr(s, ',')) != NULL)
*s1 = '\0';
else {
last = 1;
s1 = s;
}
if ((s2 = strchr(s, ':')) != NULL)
*s2 = '\0';
if (icb_addgroup(NULL, s, s2 ? ++s2 : NULL) == NULL)
err(EX_UNAVAILABLE, NULL);
s = ++s1;
s1 = s2 = NULL;
}
}
time_t
getmonotime(void)
{
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
syslog(LOG_ERR, "%m");
exit(EX_OSERR);
}
return (ts.tv_sec);
}
void
getpeerinfo(struct icb_session *is)
{
struct sockaddr_storage ss;
socklen_t ss_len = sizeof ss;
bzero(&ss, sizeof ss);
if (getpeername(EVBUFFER_FD(is->bev), (struct sockaddr *)&ss,
&ss_len) != 0)
return;
is->port = 0;
switch (ss.ss_family) {
case AF_INET:
is->port = ntohs(((struct sockaddr_in *)&ss)->sin_port);
break;
case AF_INET6:
is->port = ntohs(((struct sockaddr_in6 *)&ss)->sin6_port);
break;
}
dns_rresolv(is, &ss);
}

32
icbd.h Normal file
View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2009 Mike Belopuhov
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define ICBD_USER "_icbd"
#define TCP_BACKLOG 5
#define EVBUFFER_FD(x) (EVENT_FD(&(x)->ev_read))
extern int verbose;
/* icbd.c */
time_t getmonotime(void);
/* dns.c */
struct sockaddr_storage;
int icbd_dns_init(void);
void icbd_dns(int, short, void *);
int dns_rresolv(struct icb_session *, struct sockaddr_storage *);