/*
 * 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 */
