/*
* brocas.c - IP Messenger 1.20 protocol
* Copyright (C) 1996, 1997 by Toshihiro Kanda.
*/
char rcsid_brocas[] = "$Id: brocas.c,v 3.7 1997/05/02 05:47:43 candy Exp candy $";
#include <ctype.h>
#ifdef USE_VARARGS
#include <varargs.h>
#else
#include <stdarg.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/time.h> /* setitimer() */
/* includes below are order dependent */
#include <sys/param.h> /* htons() */
#include <sys/types.h> /* socket() */
#include <sys/socket.h> /* socket() */
#include <netinet/in.h> /* inet_addr() INADDR_ANY */
#include <arpa/inet.h> /* inet_addr() */
#ifdef SOCKS
#include <socks.h>
#endif
#include "xipmsg.h"
#include "kanji.h"
#include "brocas.h"
#ifdef BOGUS_REALLOC
#undef realloc
#define realloc(p,s) ((p)?((realloc)(p,s)):malloc(s))
#endif
#ifdef NO_SSIZE_T
#define ssize_t int
#endif
#ifdef NO_MEMMOVE
#define memmove(d,s,l) bcopy(s,d,l)
#endif
#include "dyna.h"
#if defined BSD4_4
#define SENDTO(s,msg,len,flags,to,tolen) sendto(s,msg,len,flags,to,tolen)
#define SETSOCKOPT(s,level,optname,optval,optlen) setsockopt(s,level,optname,optval,optlen)
#else
#define SENDTO(s,msg,len,flags,to,tolen) sendto(s,msg,len,flags,(struct sockaddr *)to,tolen)
#define SETSOCKOPT(s,level,optname,optval,optlen) setsockopt(s,level,optname,(char *)optval,optlen)
#endif
int debug_flag;
static int bro_socket;
static int bro_port;
static char *bro_user; /* SJIS */
static const char *bro_host;
static int bro_disabled;
static unsigned long bro_tic;
char *myname;
#define HH(s) ((int)(((s) % 86400)/3600))
#define MM(s) ((int)(((s) % 3600)/60))
#define SS(s) ((int)((s) % 60))
/*
*
*/
static int
#ifdef USE_VARARGS
warning(va_alist)
va_dcl
#else
warning(const char *fmt, ...)
#endif
{
struct timeval tv;
va_list ap;
#ifdef USE_VARARGS
const char *fmt;
va_start(ap);
fmt = va_arg(ap, const char *);
#else
va_start(ap, fmt);
#endif
gettimeofday(&tv, NULL);
fprintf(stderr, "%02d:%02d:%02d.%06lu ", HH(tv.tv_sec), MM(tv.tv_sec), SS(tv.tv_sec), tv.tv_usec);
vfprintf(stderr, fmt, ap);
va_end(ap);
return 0;
}/* warning */
/*
* 文字列を len バイト以内にコピーし、'\0' ターミネートする。
*/
char *
strncpyz(char *dst, const char *src, size_t len)
{
strncpy(dst, src, len);
dst[len - 1] = '\0';
return dst;
}/* strncpyz */
/*
*
*/
static int
#ifdef USE_VARARGS
error(va_alist)
va_dcl
#else
error(const char *fmt, ...)
#endif
{
va_list ap;
#ifdef USE_VARARGS
const char *fmt;
va_start(ap);
fmt = va_arg(ap, const char *);
#else
va_start(ap, fmt);
#endif
fprintf(stderr, "%s: ", myname);
vfprintf(stderr, fmt, ap);
fprintf(stderr, ": ");
perror(NULL);
va_end(ap);
return 0;
}/* error */
/*
* 文字列を malloc() して複製する。
* あとで free(3) してちょ。
*/
char *
str_dup(const char *s)
{
char *p = malloc(strlen(s) + 1);
if (p != NULL)
strcpy(p, s);
return p;
}/* str_dup */
/*
* 文字列の n 個目の ch の位置を返す。
* n == 1 なら strchr() と同じ。n == 0 なら NULL
*/
char *
strnchr(const char *s, int ch, size_t n)
{
if (n == 0)
s = NULL;
while (s != NULL && n-- != 0) {
s = strchr(s, ch);
if (s != NULL && *s != '\0' && n != 0)
s++;
}/* while */
return (char *)s;
}/* strnchr */
/*
* 文字列同士を tolower() しながら比較する。
*/
int
strcmpi(const char *d_, const char *s_)
{
const unsigned char *d = (const unsigned char *)d_, *s = (const unsigned char *)s_;
int cp;
while ((cp = tolower(*d) - tolower(*s)) == 0 && *d != '\0') {
d++;
s++;
}/* while */
return cp;
}/* strcmpi */
/*
* 文字列同士を tolower() しながら、長くとも n バイト比較する。
*/
int
strncmpi(const char *d_, const char *s_, size_t n)
{
const unsigned char *d = (const unsigned char *)d_, *s = (const unsigned char *)s_;
int cp = 0;
while (n-- != 0 && (cp = tolower(*d) - tolower(*s)) == 0 && *d != '\0') {
d++;
s++;
}/* while */
return cp;
}/* strcmpi */
/*
* 末尾の空白文字の列を無くす。
*/
static char *
strtrim(char *str)
{
char *p = strchr(str, '\0');
while (p-- != str && isascii(*p) && isspace(*p))
*p = '\0';
return str;
}/* strtrim */
/*
* シフトJIS 文字列を EUC にして複製する。
* あとで free(3) してちょ。
*/
static char *
strstedup(const char *s)
{
size_t len = strstoelen(s);
char *p = malloc(len + 1);
if (p != NULL)
strstoe(p, s);
return p;
}/* strstedup */
#if 0 /* [ */
/*
* bit 並びを反転させながらの memcpy
*/
static unsigned char *
rev_memcpy(void *d_, const void *s_, size_t n)
{
static unsigned char byte_rev[256] = {
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
};
int i;
unsigned char *d = d_;
const unsigned char *s = s_;
for (i = 0; i < n; i++)
*d++ = byte_rev[*s++];
return d_;
}/* rev_memcpy */
#endif /* ] */
/*
* EUC の 2 byte もじをぶった切らないように、 column で改行する。
*/
static char *
wrap_euc(char *d_, const char *s_, int column)
{
unsigned char *d = (unsigned char *)d_;
const unsigned char *s = (const unsigned char *)s_;
size_t c = 0;
while (*s != '\0') {
if (*s == '\n') {
*d++ = *s++;
c = 0;
}
else if (is1euc(*s)) {
if ((c + 2) > column) {
*d++ = '\n';
c = 0;
}
*d++ = *s++;
c++;
if (*s != '\0') {
*d++ = *s++;
c++;
}
}
else {
if ((c + 1) > column) {
*d++ = '\n';
c = 0;
}
*d++ = *s++;
c++;
}
}/* while */
*d = '\0';
return d_;
}/* wrap_euc */
/*
* wrap_euc() する時の長さ。
*/
static size_t
wrap_euclen(const char *s_, int column)
{
size_t ins = 0, c = 0;
const unsigned char *s = (const unsigned char *)s_;
while (*s != '\0') {
if (*s == '\n') {
s++;
c = 0;
}
else if (is1euc(*s)) {
if ((c + 2) > column) {
ins++;
c = 0;
}
s++;
c++;
if (*s != '\0') {
s++;
c++;
}
}
else {
if ((c + 1) > column) {
ins++;
c = 0;
}
s++;
c++;
}
}/* while */
return strlen(s_) + ins;
}/* wrap_euclen */
/*
* コールバック [
*/
struct bro_cb_t {
bro_callback_t cb_proc;
void *cb_closure;
};
struct bro_cb_t bro_cbacks[BRO_EV_MAX];
/*
* イベント e が起きた時のコールバックルーチンと、
* クライアントデータ closure を登録する。
*/
int
bro_add_callback(enum bro_event_t e, bro_callback_t callback, void *closure)
{
int err = -1;
if (e < BRO_EV_MAX) {
bro_cbacks[e].cb_proc = callback;
bro_cbacks[e].cb_closure = closure;
err = 0;
}
return err;
}/* bro_add_callback */
/*
* イベント e に対応するコールバックルーチンを呼び出す。
*/
static int
call_cback(enum bro_event_t e, void *call_data)
{
int err = -1;
if (e < BRO_EV_MAX) {
bro_callback_t cback = bro_cbacks[e].cb_proc;
if (cback != NULL) {
cback(e, bro_cbacks[e].cb_closure, call_data);
}
}
return err;
}/* call_cback */
/* ] コールバック */
/*
* パケット再送信 [
*/
#define XQ_REXMT 2 /* 再送する間隔 [500msec 単位] */
#define XQ_KEEP 40 /* あきらめる時間 [500msec 単位] */
struct xq_t {
struct xq_t *xq_next;
unsigned long xq_id; /* 識別子 */
int xq_so;
union saddr xq_saddr;
void *xq_buf; /* malloc()'ed */
size_t xq_size;
int xq_keep; /* 0 になったら、あきらめる。 */
int xq_rexmt; /* 0 になったら、再送信する。 */
int xq_rexmt0;
int xq_givenup; /* あきらめたフラグ */
};
/*
* 構造体を初期化する。
*/
static struct xq_t *
xq_initone(struct xq_t *xq)
{
memset(xq, '\0', sizeof(*xq));
xq->xq_rexmt0 = XQ_REXMT;
xq->xq_rexmt = XQ_REXMT;
xq->xq_keep = XQ_KEEP;
return xq;
}/* xq_initone */
struct xq_t *xq_base; /* キュー */
/*
* キューに入れる。
*/
struct xq_t *
xq_enq(struct xq_t *xq)
{
xq->xq_next = xq_base;
xq_base = xq;
return xq;
}/* xq_enq */
/*
* キューから外す。
*/
static void
xq_deq(struct xq_t *dst)
{
if (dst != NULL) {
if (dst == xq_base) {
xq_base = dst->xq_next;
}
else {
struct xq_t *xq = xq_base;
while (xq != NULL && xq->xq_next != dst)
xq = xq->xq_next;
if (xq != NULL)
xq->xq_next = dst->xq_next;
}
}
}/* xq_deq */
/*
* 識別子 id を持つエントリを探す。
*/
static struct xq_t *
xq_lookup(unsigned long id)
{
struct xq_t *xq = xq_base;
while (xq != NULL && xq->xq_id != id)
xq = xq->xq_next;
return xq;
}/* xq_lookup */
/*
* パケット送信する。
*/
static int
xq_transmit(struct xq_t *xq)
{
int flags = 0;
int err = SENDTO(xq->xq_so, xq->xq_buf, xq->xq_size, flags, &xq->xq_saddr.sa, sizeof(xq->xq_saddr.sin));
if (err < 0)
error("sendto");
if (debug_flag & 2)
warning("xq_xmit(%p %lu)\n", xq, xq->xq_id);
return err;
}/* xq_transmit */
static int xq_givenup;
/*
* 500msec 毎に呼び出される、タイマルーチン。
*/
static void
xq_timers(void)
{
struct xq_t *xq = xq_base;
while (xq != NULL) {
if (!xq->xq_givenup) {
if (--(xq->xq_keep) == 0) {
/* あきらめる。 */
xq->xq_givenup = 1;
if (xq_givenup++ == 0)
call_cback(BRO_EV_START_WORK_PROC, NULL);
}
else if (--(xq->xq_rexmt) == 0) {
/* 再送信する。 */
xq_transmit(xq);
xq->xq_rexmt0 = xq->xq_rexmt0 * 3 / 2;
xq->xq_rexmt = xq->xq_rexmt0;
}
}
xq = xq->xq_next;
}/* while */
}/* xq_timers */
/*
*
*/
static void
xq_work_proc(void)
{
if (xq_givenup) {
struct xq_t *xq = xq_base;
while (xq != NULL) {
struct xq_t *next = xq->xq_next;
if (xq->xq_givenup) {
char lbuf[64], *p;
size_t size;
memset(lbuf, '\0', sizeof(lbuf));
sprintf(lbuf, "%16s.%d ", inet_ntoa(xq->xq_saddr.sin.sin_addr), ntohs(xq->xq_saddr.sin.sin_port));
p = strchr(lbuf, '\0');
size = sizeof(lbuf) - (p - lbuf);
memcpy(p, xq->xq_buf, MIN(xq->xq_size, size - 1));
call_cback(BRO_EV_NO_ACK, lbuf);
xq_deq(xq);
free(xq->xq_buf);
free(xq);
}
xq = next;
}/* while */
xq_givenup = 0;
}
}/* xq_work_proc */
/* ] パケット再送信 */
static size_t
iov_total(const struct iov_t *v)
{
size_t total = 0;
while (v != NULL) {
total += v->iov_len;
v = v->iov_next;
}/* while */
return total;
}/* iov_total */
static void *
iov_gather(void *buf_, const struct iov_t *v)
{
char *buf = buf_;
while (v != NULL) {
memcpy(buf, v->iov_base, v->iov_len);
buf += v->iov_len;
v = v->iov_next;
}/* while */
return buf_;
}/* iov_gather */
/*
*
*/
static int
p_sendtov(int so, const union saddr *to, unsigned long pno, const struct iov_t *iov)
{
int err = -1;
struct xq_t *xq = malloc(sizeof(*xq));
if (xq != NULL) {
size_t total = iov_total(iov);
char *buf = malloc(total);
if (buf != NULL) {
iov_gather(buf, iov);
xq_initone(xq);
xq->xq_id = pno;
xq->xq_so = so;
xq->xq_saddr = *to;
xq->xq_buf = buf;
xq->xq_size = total;
err = xq_transmit(xq);
if (err >= 0)
xq_enq(xq);
else {
free(xq->xq_buf);
free(xq);
}
}
}
return err;
}/* p_sendtov */
/*
* ソケット so からパケットを受信する。
*/
static ssize_t
p_get(int so, union saddr *from, int msec, void *buf, size_t size)
{
ssize_t err;
struct timeval tv;
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(so, &readfds);
tv.tv_sec = msec / 1000;
tv.tv_usec = (msec % 1000) * 1000;
err = select(so + 1, &readfds, NULL, NULL, &tv);
if (err > 0) {
int flags = 0, fromlen = sizeof(from->sa);
err = recvfrom(so, buf, size, flags, &from->sa, &fromlen);
if (err < 0)
perror("recvfrom");
}
return err;
}/* p_get */
static ssize_t
sendtov(int so, int flags, const struct sockaddr *to, int tolen, const struct iov_t *iov)
{
ssize_t err = -1;
size_t total = iov_total(iov);
char *buf = malloc(total);
if (buf != NULL) {
iov_gather(buf, iov);
err = SENDTO(so, buf, total, flags, to, tolen);
if (err < 0)
error("sendto");
free(buf);
}
return err;
}/* sendtov */
static unsigned long msg_number;
static int
send_msgv(int so, const union saddr *to, unsigned long command, int retryflag, const struct iov_t *iov)
{
char lbuf[2 + 12 + USERNAME_MAX + 1 + HOSTNAME_MAX + 1 + 12];
int err, flags = 0;
struct iov_t v;
sprintf(lbuf, "1:%ld:%.*s:%.*s:%lu:", msg_number++, USERNAME_MAX, bro_user, HOSTNAME_MAX, bro_host, command);
SET_IOV(&v, (void *)iov, lbuf, strlen(lbuf));
if (retryflag) {
err = p_sendtov(so, to, msg_number - 1, &v);
}
else {
err = sendtov(so, flags, &to->sa, sizeof(to->sin), &v);
}
return err;
}/* send_msgv */
static int
send_msg(int so, const union saddr *to, unsigned long command, const char *msg)
{
char lbuf[2 + 12 + USERNAME_MAX + 1 + HOSTNAME_MAX + 1 + 12];
int err, flags = 0;
struct iov_t v[2];
sprintf(lbuf, "1:%ld:%.*s:%.*s:%lu:", msg_number++, USERNAME_MAX, bro_user, HOSTNAME_MAX, bro_host, command);
SET_IOV(&v[0], &v[1], lbuf, strlen(lbuf));
SET_IOV(&v[1], NULL, (void *)msg, strlen(msg) + 1);
err = sendtov(so, flags, &to->sa, sizeof(to->sin), v);
return err;
}/* send_msg */
/*
*/
int
bro_send(const char *msg, const unsigned char *icon, const struct maddr_t *ma)
{
int err = -1;
char *sjis = malloc(strlen(msg) + 1);
if (sjis != NULL) {
unsigned long cmd = IPMSG_SENDMSG | IPMSG_SENDCHECKOPT;
int retry = 1;
struct iov_t v[2];
stretos(sjis, msg);
SET_IOV(&v[0], &v[1], sjis, strlen(sjis) + 1);
SET_IOV(&v[1], NULL, (void *)icon, 128);
if (send_msgv(ma->m_so, &ma->m_saddr, cmd, retry, v) > 0)
err = 0;
free(sjis);
}
return err;
}/* bro_send */
/*
* 名前リストの管理 [
*/
struct dyns_t STRUCT_DYNA(struct ns_t);
struct dyns_t dyns;
/*
* INET ソケットアドレスの大小比較
*/
static int
cmp_sin(const struct sockaddr_in *d, const struct sockaddr_in *s)
{
unsigned long da = d->sin_addr.s_addr, sa = s->sin_addr.s_addr;
int cmp = da == sa ? 0 : (da < sa ? -1 : 1);
#if 0 /* port 番号は比較対象としない */
if (cmp == 0) {
cmp = ntohs(d->sin_port) - ntohs(s->sin_port);
}
#endif
return cmp;
}/* cmp_sin */
/*
*
*/
static int
ns_cmp(const void *d_, const void *s_)
{
const struct ns_t *d = d_, *s = s_;
const struct maddr_t *dm = &d->ns_maddr, *sm = &s->ns_maddr;
int cmp = strcmpi(dm->m_user, sm->m_user);
if (cmp == 0) {
cmp = cmp_sin(&dm->m_saddr.sin, &sm->m_saddr.sin);
}
return cmp;
}/* ns_cmp */
/*
* 名前リストから検索する
*/
struct ns_t *
ns_lookup(const struct maddr_t *ma)
{
struct ns_t *ret = NULL;
struct ns_t *ls = DYNA_BUF(&dyns);
if (ls != NULL) {
int n = DYNA_USED(&dyns);
struct ns_t key;
key.ns_maddr = *ma;
ret = bsearch(&key, ls, n, sizeof(*ls), ns_cmp);
}
return ret;
}/* ns_lookup */
/*
*
*/
struct ns_t *
ns_get(int idx)
{
struct ns_t *ret = NULL;
struct ns_t *ls = DYNA_BUF(&dyns);
if (ls != NULL) {
int n = DYNA_USED(&dyns);
if (idx >= 0 && idx < n)
ret = &ls[idx];
}
return ret;
}/* ns_get */
/*
* 新しいエントリを作る。
*/
static struct ns_t *
ns_new(const struct maddr_t *ma)
{
struct ns_t *ret = DYNA_NEXT(&dyns);
if (ret != NULL) {
struct ns_t key;
struct ns_t *ls = DYNA_BUF(&dyns);
int i = 0, n = DYNA_USED(&dyns) - 1;
key.ns_maddr = *ma;
while (i < n && ns_cmp(&key, &ls[i]) > 0) {
i++;
}/* while */
memmove(&ls[i + 1], &ls[i], sizeof(*ls) * (n - i));
ret = &ls[i];
memset(ret, '\0', sizeof(*ret));
}
return ret;
}/* ns_new */
/*
*
*/
void
ns_free(const struct maddr_t *ma)
{
struct ns_t *ns = ns_lookup(ma);
if (ns != NULL) {
struct ns_t *ls = DYNA_BUF(&dyns);
int i = ns - ls, n = DYNA_USED(&dyns);
memmove(&ls[i], &ls[i + 1], sizeof(*ls) * (n - i - 1));
DYNA_UNGROW(&dyns, 1);
}
}/* ns_free */
/*
*
*/
void
ns_clear(void)
{
DYNA_RESET(&dyns);
}/* ns_clear */
/*
*
*/
char **
ns_list(void)
{
int n = DYNA_USED(&dyns);
static char **last;
char **ls = malloc(sizeof(*ls) * (n + 1));
if (last != NULL) {
char **mv = last;
while (*mv != NULL)
free(*mv++);
free(last);
last = NULL;
}
if (ls != NULL) {
int i;
struct ns_t *ns = DYNA_BUF(&dyns);
for (i = 0; i < n; i++, ns++) {
size_t len = strlen(ns->ns_nick) + 1 + strlen(ns->ns_host);
ls[i] = malloc(len + 1);
if (ls[i] != NULL) {
strcat(strcat(strcpy(ls[i], ns->ns_nick), "@"), ns->ns_host);
}
}/* for */
ls[n] = NULL;
}
last = ls;
return ls;
}/* ns_list */
/* ] 名前リストの管理 */
/*
* 同一パケットのフィルタリング [
*
* 名前リストのエントリ毎に受信パケットリストを持つ。
* 最も最近受信したパケットがリストの先頭にくるようにする。
* 受信後一定時間経ったらリストから削除する。
*/
#define RCV_KEEP (60*2) /* 保持する時間 [tic] */
/*
* パケット番号 id が受信リストにあるかどうか調べる。
*/
static int
rcv_id_lookup(struct rcv_t *rcv, unsigned long id)
{
int idx = 0;
struct rcv_q_t *q = rcv->rcv_q;
while (idx < rcv->rcv_used && q[idx].q_id != id)
idx++;
if (idx >= rcv->rcv_used)
idx = -1;
return idx;
}/* rcv_id_lookup */
/*
* パケット番号 id と、保持する時間を受信リストに記録する。
*/
static int
rcv_add(struct rcv_t *rcv, unsigned long id)
{
int err = -1;
struct rcv_q_t *q = rcv->rcv_q;
size_t size = rcv->rcv_size;
if (rcv->rcv_used >= size) {
size = size == 0 ? 64 : size * 2;
q = realloc(q, size);
if (q != NULL) {
rcv->rcv_q = q;
rcv->rcv_size = size;
}
}
if (q != NULL) {
memmove(q + 1, q, sizeof(*q) * rcv->rcv_used);
rcv->rcv_used++;
q->q_keep = bro_tic + RCV_KEEP;
q->q_id = id;
if (debug_flag & 2)
warning("add: %lu %lu\n", q->q_id, q->q_keep);
err = 0;
}
return err;
}/* rcv_add */
/*
* パケット番号 id をリストの先頭に移動する。
*/
static int
rcv_raise(struct rcv_t *rcv, unsigned long id)
{
int i = rcv_id_lookup(rcv, id);
if (i >= 0) {
struct rcv_q_t *q = rcv->rcv_q;
struct rcv_q_t x = q[i];
memmove(&q[1], q, i);
*q = x;
q->q_keep = bro_tic + RCV_KEEP;
if (debug_flag & 2)
warning("raise: %lu %lu\n", q->q_id, q->q_keep);
}
return 0;
}/* rcv_raise */
/*
* 一定時間経ったものは削除する。
*/
static int
rcv_delete(struct rcv_t *rcv)
{
struct rcv_q_t *q = rcv->rcv_q;
int i = rcv->rcv_used;
while (--i >= 0 && q[i].q_keep < bro_tic)
if (debug_flag & 2)
warning("delete: %lu %lu %lu\n", bro_tic, q[i].q_keep, q[i].q_id);
i++;
if (i + 1 < rcv->rcv_used)
memmove(&q[i], &q[i + 1], sizeof(*q) * (rcv->rcv_used - (i + 1)));
rcv->rcv_used = i;
return 0;
}/* rcv_delete */
/* ] 同一パケットのフィルタリング */
/*
* ネットワークの登録
*/
struct dynet_t STRUCT_DYNA(union saddr);
struct dynet_t dynet;
static int
network_add(const char *broad)
{
union saddr *net;
net = DYNA_NEXT(&dynet);
if (net != NULL) {
memset(net, '\0', sizeof(*net));
net->sin.sin_family = AF_INET;
net->sin.sin_addr.s_addr = inet_addr(broad);
net->sin.sin_port = htons(bro_port);
}
return 0;
}/* network_add */
static int
network_bro_send(int so, unsigned long cmd, const char *msg)
{
union saddr *sin = DYNA_BUF(&dynet);
if (sin != NULL) {
int i, err;
for (i = 0; i < DYNA_USED(&dynet); i++) {
err = send_msg(so, sin, cmd, msg);
sin++;
}/* for */
}
return 0;
}/* network_bro_send */
/*
*/
int
send_IPMSG_READMSG(struct maddr_t *replyto, unsigned long pkno)
{
char lbuf[20];
sprintf(lbuf, "%lu", pkno);
send_msg(replyto->m_so, &replyto->m_saddr, IPMSG_READMSG, lbuf);
return 0;
}/* send_IPMSG_READMSG */
/*
* 起動時など、BR_ENTRY をブロードキャストする。
*/
int
send_IPMSG_BR_ENTRY(void)
{
network_bro_send(bro_socket, IPMSG_BR_ENTRY, "");
return 0;
}/* send_IPMSG_BR_ENTRY */
/*
* 終了時、BR_EXIT をブロードキャストする。
*/
int
send_IPMSG_BR_EXIT(void)
{
network_bro_send(bro_socket, IPMSG_BR_EXIT, "");
return 0;
}/* send_IPMSG_BR_EXIT */
/*
* BR_ENTRY を受け取ったら、それに ANSENTRY を返す。
*/
static int
reply_IPMSG_ANSENTRY(int so, const union saddr *from)
{
int err = 0;
if (!bro_disabled)
err = send_msg(so, from, IPMSG_ANSENTRY, "");
return err;
}/* reply_IPMSG_ANSENTRY */
/*
* 受信メッセージにオプション IPMSG_SENDCHECKOPT がついていたら
* 確認パケットを返す。
*/
static int
reply_IPMSG_RECVMSG(int so, const union saddr *from, unsigned long pkno)
{
char lbuf[32];
int err;
sprintf(lbuf, "%lu", pkno);
err = send_msg(so, from, IPMSG_RECVMSG, lbuf);
return err;
}/* reply_IPMSG_RECVMSG */
/*
* 受け取ったパケットの送り手は、すべて名前リストに登録する。
*/
static struct ns_t *
add_ns_entry(const struct maddr_t *ma, const char *host, const char *nick, const char *aux)
{
struct ns_t *ns = ns_lookup(ma);
if (ns == NULL) {
ns = ns_new(ma);
}
if (ns != NULL) {
ns->ns_maddr = *ma;
strncpyz(ns->ns_host, host, sizeof(ns->ns_host));
if (nick != NULL)
strncpyz(ns->ns_nick, nick, sizeof(ns->ns_nick));
if (ns->ns_nick[0] == '\0' && aux != NULL)
strncpyz(ns->ns_nick, aux, sizeof(ns->ns_nick));
call_cback(BRO_EV_LIST_CHANGED, NULL);
}
return ns;
}/* add_ns_entry */
static void
delete_ns_entry(const struct maddr_t *ma)
{
ns_free(ma);
}/* delete_ns_entry */
/*
* パケット処理部はここから [
*/
static int
recv_IPMSG_NOOPERATION(const struct packet_t *pk)
{
return 0;
}/* recv_IPMSG_NOOPERATION */
/*
* 新規ノードが立ち上がった
* pk->pk_msg == ニックネーム
*/
static int
recv_IPMSG_BR_ENTRY(const struct packet_t *pk)
{
char *nick = pk->pk_msg[0] != '\0' ? pk->pk_msg : pk->pk_user;
reply_IPMSG_ANSENTRY(pk->pk_maddr.m_so, &pk->pk_maddr.m_saddr);
add_ns_entry(&pk->pk_maddr, pk->pk_host, nick, NULL);
call_cback(BRO_EV_LIST_CHANGED, NULL);
return 0;
}/* recv_IPMSG_BR_ENTRY */
/*
* ノード終了した。
* pk->pk_msg == ニックネーム
*/
static int
recv_IPMSG_BR_EXIT(const struct packet_t *pk)
{
delete_ns_entry(&pk->pk_maddr);
call_cback(BRO_EV_LIST_CHANGED, NULL);
return 0;
}/* recv_IPMSG_BR_EXIT */
/*
* BR_ENTRY の返事が来た。
* pk->pk_msg == ニックネーム
*/
static int
recv_IPMSG_ANSENTRY(const struct packet_t *pk)
{
char *nick = pk->pk_msg[0] != '\0' ? pk->pk_msg : pk->pk_user;
add_ns_entry(&pk->pk_maddr, pk->pk_host, nick, NULL);
call_cback(BRO_EV_LIST_CHANGED, NULL);
return 0;
}/* recv_IPMSG_ANSENTRY */
static int
recv_IPMSG_ABSENCE(const struct packet_t *pk)
{
return 0;
}/* recv_IPMSG_ABSENCE */
static int
recv_IPMSG_BR_ISGETLIST(const struct packet_t *pk)
{
return 0;
}/* recv_IPMSG_BR_ISGETLIST */
static int
recv_IPMSG_OKGETLIST(const struct packet_t *pk)
{
return 0;
}/* recv_IPMSG_OKGETLIST */
static int
recv_IPMSG_GETLIST(const struct packet_t *pk)
{
return 0;
}/* recv_IPMSG_GETLIST */
static int
recv_IPMSG_ANSLIST(const struct packet_t *pk)
{
return 0;
}/* recv_IPMSG_ANSLIST */
/*
* メッセージが届いた。
*/
static int
recv_IPMSG_SENDMSG(const struct packet_t *pk)
{
char *nick = pk->pk_user;
struct ns_t *ns = add_ns_entry(&pk->pk_maddr, pk->pk_host, NULL, nick);
unsigned long opt = GET_OPT(pk->pk_cmd);
int ignore = 0;
if (ns != NULL) {
nick = ns->ns_nick;
rcv_delete(&ns->ns_rcv);
if (rcv_id_lookup(&ns->ns_rcv, pk->pk_no) < 0)
rcv_add(&ns->ns_rcv, pk->pk_no);
else {
rcv_raise(&ns->ns_rcv, pk->pk_no);
ignore = 1;
}
}
if ((opt & IPMSG_SENDCHECKOPT) && !(opt & (IPMSG_BROADCASTOPT | IPMSG_AUTORETOPT)))
reply_IPMSG_RECVMSG(pk->pk_maddr.m_so, &pk->pk_maddr.m_saddr, pk->pk_no);
if (!ignore) {
#define BRO_COLUMNS 58
size_t wraplen = wrap_euclen(pk->pk_msg, BRO_COLUMNS);
char *wrapbuf = malloc(wraplen + 1);
if (wrapbuf != NULL) {
char idname[USERNAME_MAX + HOSTNAME_MAX];
struct msg_data_t md;
wrap_euc(wrapbuf, pk->pk_msg, BRO_COLUMNS);
strtrim(wrapbuf);
strcat(strcat(strncpyz(idname, nick, USERNAME_MAX), "@"), ns->ns_host);
md.md_replyto = (void *)&pk->pk_maddr;
md.md_msg = wrapbuf;
md.md_icon = pk->pk_icon;
md.md_opt = opt;
md.md_pkno = pk->pk_no;
md.md_from = idname;
call_cback(BRO_EV_RECV_MESSAGE, &md);
free(wrapbuf);
}
else
error("malloc");
}
return 0;
}/* recv_IPMSG_SENDMSG */
/*
* 送ったメッセージの受信確認パケットが帰って来た。
* msg == 送ったメッセージの番号
*/
static int
recv_IPMSG_RECVMSG(const struct packet_t *pk)
{
struct xq_t *xq = xq_lookup(strtol(pk->pk_msg, NULL, 10));
if (xq != NULL) {
char lbuf[80];
xq_deq(xq);
sprintf(lbuf, "%16s.%d ", inet_ntoa(xq->xq_saddr.sin.sin_addr), ntohs(xq->xq_saddr.sin.sin_port));
call_cback(BRO_EV_RECV_ACK, lbuf);
free(xq->xq_buf);
free(xq);
}
return 0;
}/* recv_IPMSG_RECVMSG */
static int
recv_IPMSG_READMSG(const struct packet_t *pk)
{
return 0;
}/* recv_IPMSG_READMSG */
static int
recv_IPMSG_DELMSG(const struct packet_t *pk)
{
return 0;
}/* recv_IPMSG_DELMSG */
static int
recv_IPMSG_GETINFO(const struct packet_t *pk)
{
return 0;
}/* recv_IPMSG_GETINFO */
static int
recv_IPMSG_SENDINFO(const struct packet_t *pk)
{
return 0;
}/* recv_IPMSG_SENDINFO */
/*
*
*/
static int
do_cmd(struct packet_t *pk)
{
unsigned long cmd = GET_MODE(pk->pk_cmd);
switch (cmd) {
case IPMSG_NOOPERATION: /* 無操作 */
recv_IPMSG_NOOPERATION(pk);
break;
case IPMSG_BR_ENTRY: /* サービスにエントリー(起動時にBroadcast) */
recv_IPMSG_BR_ENTRY(pk);
break;
case IPMSG_BR_EXIT: /* サービスから抜ける(終了時にBroadcast) */
recv_IPMSG_BR_EXIT(pk);
break;
case IPMSG_ANSENTRY: /* サービスにエントリーを認識 */
recv_IPMSG_ANSENTRY(pk);
break;
case IPMSG_BR_ABSENCE: /* 不在モード変更 */
recv_IPMSG_ABSENCE(pk);
break;
case IPMSG_BR_ISGETLIST: /* ホストリスト送出可能メンバの探索 */
recv_IPMSG_BR_ISGETLIST(pk);
break;
case IPMSG_OKGETLIST: /* ホストリスト送出可能通知 */
recv_IPMSG_OKGETLIST(pk);
break;
case IPMSG_GETLIST: /* ホストリスト送出要求 */
recv_IPMSG_GETLIST(pk);
break;
case IPMSG_ANSLIST: /* ホストリスト送出 */
recv_IPMSG_ANSLIST(pk);
break;
case IPMSG_SENDMSG: /* メッセージの送信 */
recv_IPMSG_SENDMSG(pk);
break;
case IPMSG_RECVMSG: /* メッセージの受信確認 */
recv_IPMSG_RECVMSG(pk);
break;
case IPMSG_READMSG: /* 封書の開封通知 */
recv_IPMSG_READMSG(pk);
break;
case IPMSG_DELMSG: /* 封書破棄通知 */
recv_IPMSG_DELMSG(pk);
break;
case IPMSG_GETINFO:
recv_IPMSG_GETINFO(pk);
break;
case IPMSG_SENDINFO:
recv_IPMSG_SENDINFO(pk);
break;
}/* switch */
return 0;
}/* do_cmd */
static struct packet_t *
decode_packet(char *lbuf, size_t size, struct packet_t *pk)
{
/* Ver(1):Packet番号 : 自User名 : 自Host名 : Command番号 : 追加部*/
struct packet_t *ret = NULL;
char *user, *host, *msg;
sscanf(lbuf, "%lu:%lu:%*[^:]:%*[^:]:%lu", &pk->pk_ver, &pk->pk_no, &pk->pk_cmd);
user = strnchr(lbuf, ':', 2);
if (user != NULL) {
user++;
host = strnchr(lbuf, ':', 3);
if (host != NULL) {
host++;
msg = strnchr(lbuf, ':', 5);
if (msg != NULL) {
msg++;
*strchr(user, ':') = '\0';
*strchr(host, ':') = '\0';
pk->pk_user = user;
pk->pk_host = host;
pk->pk_msg = msg;
ret = pk;
}
}
}
return ret;
}/* decode_packet */
/*
* パケットが届いた時呼び出される。
*/
int
bro_recv_packet(int so)
{
struct packet_t pk;
char sbuf[MESSAGE_MAX];
union saddr *from = &pk.pk_maddr.m_saddr;
ssize_t size = p_get(so, from, 0, sbuf, sizeof(sbuf) - 1);
if (size > 0) {
char ebuf[sizeof(sbuf) * 2], *p;
unsigned char icon[128];
size_t iconsize;
sbuf[size] = '\0';
p = strchr(sbuf, '\0') + 1;
iconsize = MIN(sizeof(icon), size - (p - sbuf));
memset(icon, '\0', sizeof(icon));
memcpy(icon, p, iconsize);
strstoe(ebuf, sbuf);
size = strlen(ebuf) + 1;
if (debug_flag & 2) {
warning("%d: %16s.%d [", so, inet_ntoa(from->sin.sin_addr), ntohs(from->sin.sin_port));
fputs(ebuf, stderr);
fputs("]\n", stderr);
}
if (decode_packet(ebuf, size, &pk) != NULL) {
pk.pk_maddr.m_so = so;
pk.pk_icon = icon;
strncpyz(pk.pk_maddr.m_user, pk.pk_user, sizeof(pk.pk_maddr.m_user));
do_cmd(&pk);
}
else
warning("bad packet.\n");
}
return 0;
}/* recv_packet */
/* ] パケット処理部おわり */
/*
* タイムアウト処理
* 1 秒毎に呼び出される。
*/
void
bro_job(void)
{
bro_tic++;
xq_timers();
}/* bro_job */
/*
* 暇な時呼び出される。
*/
void
bro_work(void)
{
xq_work_proc();
}/* bro_job */
/*
*
*/
int
bro_set_disable(int disable)
{
bro_disabled = disable;
return 0;
}/* bro_set_disable */
/*
*
*/
int
bro_init(int port, const char *user, const char *host, const char * const *bros)
{
int so;
#ifdef SOCKS
SOCKSinit(myname);
#endif
so = socket(PF_INET, SOCK_DGRAM, 0);
DYNA_IZ(&dynet, 32);
bro_user = malloc(strlen(user) + 1);
if (bro_user != NULL) {
stretos(bro_user, user);
bro_port = port;
bro_host = host;
msg_number = time(NULL);
while (*bros != NULL)
network_add(*bros++);
if (so >= 0) {
int optval = 1;
int err = SETSOCKOPT(so, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval));
if (err >= 0) {
int err;
union saddr sa;
memset(&sa, '\0', sizeof(sa));
sa.sin.sin_family = AF_INET;
sa.sin.sin_addr.s_addr = INADDR_ANY;
sa.sin.sin_port = htons(port);
err = bind(so, &sa.sa, sizeof(sa));
}
if (err < 0) {
close(so);
so = -1;
}
}
}
bro_socket = so;
return so;
}/* bro_init */
syntax highlighted by Code2HTML, v. 0.9.1