/************************************************************************
* IRC - Internet Relay Chat, iauth/mod_rfc931.c
* Copyright (C) 1998 Christophe Kalt
*
* 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef lint
static char rcsid[] = "@(#)$Id: mod_rfc931.c,v 1.16.2.1 1999/11/03 21:35:49 kalt Exp $";
#endif
#include "os.h"
#include "a_defines.h"
#define MOD_RFC931_C
#include "a_externs.h"
#undef MOD_RFC931_C
#define OPT_PROTOCOL 0x1
#define OPT_LAZY 0x2
struct _instance_data
{
u_char options;
u_int tried;
u_int connected;
u_int unx;
u_int other;
u_int bad;
u_int skipped;
u_int clean, timeout;
};
/*
* rfc931_init
*
* This procedure is called when a particular module is loaded.
* Returns NULL if everything went fine,
* an error message otherwise.
*/
char *
rfc931_init(self)
AnInstance *self;
{
struct _instance_data *dt;
dt = (struct _instance_data *) malloc(sizeof(struct _instance_data));
bzero((char *) dt, sizeof(struct _instance_data));
self->data = (void *) dt;
/* undocumented option */
if (self->opt && strstr(self->opt, "protocol"))
dt->options |= OPT_PROTOCOL;
if (self->opt && strstr(self->opt, "lazy"))
dt->options |= OPT_LAZY;
if (dt->options & (OPT_LAZY|OPT_PROTOCOL))
self->popt = "protocol,lazy";
else if (dt->options & OPT_LAZY)
self->popt = "lazy";
else if (dt->options & OPT_PROTOCOL)
self->popt = "protocol";
else
return NULL;
return self->popt;
}
/*
* rfc931_release
*
* This procedure is called when a particular module is unloaded.
*/
void
rfc931_release(self)
AnInstance *self;
{
struct _instance_data *st = self->data;
free(st);
}
/*
* rfc931_stats
*
* This procedure is called regularly to update statistics sent to ircd.
*/
void
rfc931_stats(self)
AnInstance *self;
{
struct _instance_data *st = self->data;
sendto_ircd("S rfc931 connected %u unix %u other %u bad %u out of %u",
st->connected, st->unx, st->other, st->bad, st->tried);
sendto_ircd("S rfc931 skipped %u aborted %u / %u",
st->skipped, st->clean, st->timeout);
}
/*
* rfc931_start
*
* This procedure is called to start an authentication.
* Returns 0 if everything went fine,
* -1 else otherwise (nothing to be done, or failure)
*
* It is responsible for sending error messages where appropriate.
* In case of failure, it's responsible for cleaning up (e.g. rfc931_clean
* will NOT be called)
*/
int
rfc931_start(cl)
u_int cl;
{
char *error;
int fd;
struct _instance_data *st = cldata[cl].instance->data;
if (st->options & OPT_LAZY && cldata[cl].state & A_DENY)
{
DebugLog((ALOG_D931, 0, "rfc931_start(%d): Lazy.", cl));
return -1;
}
if (cldata[cl].authuser &&
cldata[cl].authfrom < cldata[cl].instance->in)
{
DebugLog((ALOG_D931, 0,
"rfc931_start(%d): Instance %d already got the info",
cl, cldata[cl].authfrom));
return -1;
}
DebugLog((ALOG_D931, 0, "rfc931_start(%d): Connecting to %s %u", cl,
cldata[cl].itsip, 113));
st->tried += 1;
fd = tcp_connect(cldata[cl].ourip, cldata[cl].itsip, 113, &error);
if (fd < 0)
{
DebugLog((ALOG_D931, 0,
"rfc931_start(%d): tcp_connect() reported %s",
cl, error));
return -1;
}
cldata[cl].wfd = fd; /*so that rfc931_work() is called when connected*/
return 0;
}
/*
* rfc931_work
*
* This procedure is called whenever there's new data in the buffer.
* Returns 0 if everything went fine, and there is more work to be done,
* Returns -1 if the module has finished its work (and cleaned up).
*
* It is responsible for sending error messages where appropriate.
*/
int
rfc931_work(cl)
u_int cl;
{
struct _instance_data *st = cldata[cl].instance->data;
DebugLog((ALOG_D931, 0, "rfc931_work(%d): %d %d buflen=%d", cl,
cldata[cl].rfd, cldata[cl].wfd, cldata[cl].buflen));
if (cldata[cl].wfd > 0)
{
/*
** We haven't sent the query yet, the connection was just
** established.
*/
char query[32];
sprintf(query, "%u , %u\r\n", cldata[cl].itsport,
cldata[cl].ourport);
if (write(cldata[cl].wfd, query, strlen(query)) < 0)
{
/* most likely the connection failed */
DebugLog((ALOG_D931, 0,
"rfc931_work(%d): write() failed: %s", cl,
strerror(errno)));
close(cldata[cl].wfd);
cldata[cl].rfd = cldata[cl].wfd = 0;
return 1;
}
else
st->connected += 1;
cldata[cl].rfd = cldata[cl].wfd;
cldata[cl].wfd = 0;
}
else
{
/* data's in from the ident server */
char *ch;
u_char bad = 0;
cldata[cl].inbuffer[cldata[cl].buflen] = '\0';
ch = index(cldata[cl].inbuffer, '\r');
if (ch)
{
/* got all of it! */
*ch = '\0';
DebugLog((ALOG_D931, 0, "rfc931_work(%d): Got [%s]",
cl, cldata[cl].inbuffer));
if (cldata[cl].buflen > 1024)
cldata[cl].inbuffer[1024] = '\0';
if (ch = index(cldata[cl].inbuffer, '\n'))
/* delimiter for ircd<->iauth messages. */
*ch = '\0';
ch = cldata[cl].inbuffer;
while (*ch && !isdigit(*ch)) ch++;
if (!*ch || atoi(ch) != cldata[cl].itsport)
{
DebugLog((ALOG_D931, 0,
"remote port mismatch."));
ch = NULL;
}
while (ch && *ch && *ch != ',') ch++;
while (ch && *ch && !isdigit(*ch)) ch++;
if (ch && (!*ch || atoi(ch) != cldata[cl].ourport))
{
DebugLog((ALOG_D931, 0,
"local port mismatch."));
ch = NULL;
}
if (ch) ch = index(ch, ':');
if (ch) ch += 1;
while (ch && *ch && *ch == ' ') ch++;
if (ch && strncmp(ch, "USERID", 6))
{
DebugLog((ALOG_D931, 0, "No USERID."));
ch = NULL;
}
if (ch) ch = index(ch, ':');
if (ch) ch += 1;
while (ch && *ch && *ch == ' ') ch++;
if (ch)
{
int other = 0;
if (!strncmp(ch, "OTHER", 5))
other = 1;
ch = rindex(ch, ':');
if (ch) ch += 1;
while (ch && *ch && *ch == ' ') ch++;
if (ch && *ch)
{
if (cldata[cl].authuser)
free(cldata[cl].authuser);
cldata[cl].authuser = mystrdup(ch);
cldata[cl].authfrom =
cldata[cl].instance->in;
if (other)
st->other += 1;
else
{
st->unx += 1;
cldata[cl].state |= A_UNIX;
}
sendto_ircd("%c %d %s %u %s",
(other) ? 'u' : 'U', cl,
cldata[cl].itsip,
cldata[cl].itsport,
cldata[cl].authuser);
}
else
bad = 1;
}
else
bad = 1;
if (bad)
{
st->bad += 1;
if (st->options & OPT_PROTOCOL)
{
ch = cldata[cl].inbuffer;
while (*ch)
{
if (!(isalnum(*ch) ||
ispunct(*ch) ||
isspace(*ch)))
break;
ch += 1;
}
*ch = '\0';
sendto_log(ALOG_IRCD|ALOG_FLOG,
LOG_WARNING,
"rfc931: bad reply from %s[%s] to \"%u, %u\": %u, \"%s\"",
cldata[cl].host,
cldata[cl].itsip,
cldata[cl].itsport,
cldata[cl].ourport,
cldata[cl].buflen,
cldata[cl].inbuffer);
}
sendto_ircd("K %d %s :Bad RFC 931 reply", cl,
cldata[cl].itsip,
cldata[cl].itsport);
}
/*
** In any case, our job is done, let's cleanup.
*/
close(cldata[cl].rfd);
cldata[cl].rfd = 0;
return -1;
}
else
return 0;
}
return 0;
}
/*
* rfc931_clean
*
* This procedure is called whenever the module should interrupt its work.
* It is responsible for cleaning up any allocated data, and in particular
* closing file descriptors.
*/
void
rfc931_clean(cl)
u_int cl;
{
struct _instance_data *st = cldata[cl].instance->data;
st->clean += 1;
DebugLog((ALOG_D931, 0, "rfc931_clean(%d): cleaning up", cl));
/*
** only one of rfd and wfd may be set at the same time,
** in any case, they would be the same fd, so only close() once
*/
if (cldata[cl].rfd)
close(cldata[cl].rfd);
else if (cldata[cl].wfd)
close(cldata[cl].wfd);
cldata[cl].rfd = cldata[cl].wfd = 0;
}
/*
* rfc931_timeout
*
* This procedure is called whenever the timeout set by the module is
* reached.
*
* Returns 0 if things are okay, -1 if authentication was aborted.
*/
int
rfc931_timeout(cl)
u_int cl;
{
struct _instance_data *st = cldata[cl].instance->data;
st->timeout += 1;
DebugLog((ALOG_D931, 0, "rfc931_timeout(%d): calling rfc931_clean ",
cl));
rfc931_clean(cl);
return -1;
}
aModule Module_rfc931 =
{ "rfc931", rfc931_init, rfc931_release, rfc931_stats,
rfc931_start, rfc931_work, rfc931_timeout, rfc931_clean };
syntax highlighted by Code2HTML, v. 0.9.1