/* * 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 #ifdef USE_VARARGS #include #else #include #endif #include #include #include #include #include #include /* setitimer() */ /* includes below are order dependent */ #include /* htons() */ #include /* socket() */ #include /* socket() */ #include /* inet_addr() INADDR_ANY */ #include /* inet_addr() */ #ifdef SOCKS #include #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 */