home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume27
/
ytalk-3.0
/
part01
/
socket.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-08-20
|
22KB
|
914 lines
/* socket.c - socket functions */
/* NOTICE
*
* Copyright (c) 1990,1992,1993 Britt Yenne. All rights reserved.
*
* This software is provided AS-IS. The author gives no warranty,
* real or assumed, and takes no responsibility whatsoever for any
* use or misuse of this software, or any damage created by its use
* or misuse.
*
* This software may be freely copied and distributed provided that
* no part of this NOTICE is deleted or edited in any manner.
*
*/
/* Mail comments or questions to ytalk@austin.eds.com */
#include "header.h"
#include "menu.h"
#include "socket.h"
#include <sys/time.h>
#ifdef _AIX
# include <sys/select.h>
#endif
struct _talkd talkd[MAXDAEMON+1];
int daemons = 0;
static int otalk, ntalk; /* daemon numbers */
static CTL_MSG omsg; /* old talk message */
static CTL_RESPONSE orsp; /* old talk response */
static CTL_MSG42 nmsg; /* new talk message */
static CTL_RESPONSE42 nrsp; /* new talk response */
static int autofd = -1; /* auto invite socket fd */
static struct sockaddr_in autosock; /* auto invite socket */
static long autoid[MAXDAEMON+1]; /* auto invite seq numbers */
static u_long announce_id = 0; /* announce sequence id */
static readdr *readdr_list = NULL; /* list of re-addresses */
#define IN_ADDR(s) ((s).sin_addr.s_addr)
#define IN_PORT(s) ((s).sin_port)
#define SOCK_EQUAL(s,c) (IN_PORT(s) == IN_PORT(c) && IN_ADDR(s) == IN_ADDR(c))
/* ---- local functions ---- */
/* Create a datagram socket.
*/
static int
init_dgram(sock)
struct sockaddr_in *sock;
{
int fd, socklen;
sock->sin_family = AF_INET;
IN_ADDR(*sock) = INADDR_ANY;
IN_PORT(*sock) = 0;
if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
show_error("init_dgram: socket() failed");
bail(YTE_ERROR);
}
if(bind(fd, (struct sockaddr *)sock, sizeof(struct sockaddr_in)) != 0)
{
close(fd);
show_error("init_dgram: bind() failed");
bail(YTE_ERROR);
}
socklen = sizeof(struct sockaddr_in);
if(getsockname(fd, (struct sockaddr *)sock, &socklen) < 0)
{
close(fd);
show_error("init_dgram: getsockname() failed");
bail(YTE_ERROR);
}
IN_ADDR(*sock) = me->host_addr;
return fd;
}
/* Initialize a new daemon structure.
*/
static int
init_daemon(name, port, mptr, mlen, rptr, rlen)
char *name;
short port;
yaddr mptr, rptr;
int mlen, rlen;
{
struct servent *serv;
int d;
if(daemons >= MAXDAEMON)
{
show_error("init_daemon: too many daemons");
bail(YTE_ERROR);
}
d = ++daemons; /* daemon number zero is not defined */
if((serv = getservbyname(name, "udp")) != NULL)
talkd[d].port = serv->s_port;
else
talkd[d].port = port;
talkd[d].fd = init_dgram(&(talkd[d].sock));
talkd[d].mptr = mptr;
talkd[d].mlen = mlen;
talkd[d].rptr = rptr;
talkd[d].rlen = rlen;
return d;
}
static void
read_autoport(fd)
int fd;
{
int socklen;
static v2_pack pack;
static char estr[V2_NAMELEN + V2_HOSTLEN + 20];
static struct sockaddr_in temp;
/* accept the connection */
socklen = sizeof(struct sockaddr_in);
if((fd = accept(autofd, (struct sockaddr *) &temp, &socklen)) == -1)
{
show_error("read_autoport: accept() failed");
return;
}
/* The autoport socket just uses the old Ytalk version 2.?
* packet.
*/
errno = 0;
if(full_read(fd, &pack, V2_PACKLEN) < 0 || pack.code != V2_AUTO)
{
show_error("read_autoport: unknown auto-invite connection");
close(fd);
return;
}
close(fd);
if(!(def_flags & FL_INVITE))
{
sprintf(estr, "Talk to %s@%s?", pack.name, pack.host);
if(yes_no(estr) == 'n')
return;
}
sprintf(estr, "%s@%s", pack.name, pack.host);
invite(estr, 1); /* we should be expected */
}
/* Create and initialize the auto-invitation socket.
*/
static void
init_autoport()
{
int socklen;
autosock.sin_family = AF_INET;
IN_ADDR(autosock) = INADDR_ANY;
IN_PORT(autosock) = 0;
if((autofd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
show_error("init_autoport: socket() failed");
return;
}
if(bind(autofd, (struct sockaddr *)&autosock,
sizeof(struct sockaddr_in)) < 0)
{
close(autofd);
autofd = -1;
show_error("init_autoport: bind() failed");
return;
}
socklen = sizeof(struct sockaddr_in);
if(getsockname(autofd, (struct sockaddr *)&autosock, &socklen) < 0)
{
close(autofd);
autofd = -1;
show_error("init_autoport: getsockname() failed");
return;
}
IN_ADDR(autosock) = me->host_addr;
if(listen(autofd, 5) < 0)
{
close(autofd);
autofd = -1;
show_error("init_autoport: listen() failed");
return;
}
(void)memset(autoid, 0, (MAXDAEMON + 1) * sizeof(long));
add_fd(autofd, read_autoport);
}
/* Fill the socket address field with the appropriate return address for
* the host I'm sending to.
*/
static void
place_my_address(sock, addr)
struct sockaddr_in *sock;
register u_long addr;
{
register readdr *r;
for(r = readdr_list; r != NULL; r = r->next)
if((addr & r->mask) == r->addr)
{
addr = (r->id_addr & r->id_mask) |
(me->host_addr & (~(r->id_mask)));
IN_ADDR(*sock) = addr;
break;
}
if(r == NULL)
IN_ADDR(*sock) = me->host_addr;
sock->sin_family = htons(AF_INET);
}
/* sendit() sends the completed message to the talk daemon at the given
* hostname, then reads a response packet.
*/
static int
sendit(addr, d)
u_long addr; /* host internet address */
int d; /* daemon number */
{
int n;
struct sockaddr_in daemon;
struct timeval tv;
char *rtype, *mtype;
fd_set sel;
/* set up the appropriate message structure */
if(d == ntalk)
{
nmsg.vers = TALK_VERSION;
place_my_address(&(nmsg.ctl_addr), addr);
mtype = &(nmsg.type);
rtype = &(nrsp.type);
}
else if(d == otalk)
{
omsg.type = nmsg.type;
omsg.addr = nmsg.addr;
omsg.id_num = nmsg.id_num;
omsg.pid = nmsg.pid;
strncpy(omsg.l_name, nmsg.l_name, NAME_SIZE);
strncpy(omsg.r_name, nmsg.r_name, NAME_SIZE);
strncpy(omsg.r_tty, nmsg.r_tty, TTY_SIZE);
place_my_address(&(omsg.ctl_addr), addr);
mtype = &(omsg.type);
rtype = &(orsp.type);
}
else
{
sprintf(errstr, "Unkown daemon type: %d", d);
show_error(errstr);
return -1;
}
/* set up a sockaddr_in for the daemon we're sending to */
daemon.sin_family = AF_INET;
IN_ADDR(daemon) = addr;
IN_PORT(daemon) = talkd[d].port;
/* flush any lingering input */
FD_ZERO(&sel);
for(;;)
{
tv.tv_sec = 0L;
tv.tv_usec = 0L;
FD_SET(talkd[d].fd, &sel);
if((n = select(talkd[d].fd + 1, &sel, 0, 0, &tv)) < 0)
{
show_error("sendit: flush select() failed");
return -1;
}
if(n <= 0)
break;
if(recv(talkd[d].fd, talkd[d].rptr, talkd[d].rlen, 0) < 0)
{
show_error("sendit: flush recv() failed");
return -1;
}
}
/* Now we need to send the actual packet. Due to unreliability of
* DGRAM sockets, we must resend the packet until we get a response
* from the server. Geez... two different daemons, both on unreliable
* sockets, and maybe even different daemons on different machines.
* Is *nothing* reliable anymore???
*/
do
{
do
{
n = sendto(talkd[d].fd, talkd[d].mptr, talkd[d].mlen,
0, (struct sockaddr *) &daemon, sizeof(daemon));
if(n != talkd[d].mlen)
{
show_error("sendit: sendto() failed");
return -1;
}
tv.tv_sec = 5L;
tv.tv_usec = 0L;
FD_SET(talkd[d].fd, &sel);
if((n = select(talkd[d].fd + 1, &sel, 0, 0, &tv)) < 0)
{
show_error("sendit: first select() failed");
return -1;
}
} while (n <= 0); /* ie: until we receive a reply */
do
{
n = recv(talkd[d].fd, talkd[d].rptr, talkd[d].rlen, 0);
if(n < 0)
{
show_error("sendit: recv() failed");
return -1;
}
if(*rtype != *mtype)
tv.tv_sec = 5L;
else
tv.tv_sec = 0L;
tv.tv_usec = 0L;
FD_SET(talkd[d].fd, &sel);
if((n = select(talkd[d].fd + 1, &sel, 0, 0, &tv)) < 0)
{
show_error("sendit: second select() failed");
return -1;
}
} while(n > 0 && *rtype != *mtype);
} while(*rtype != *mtype);
/* WHEW */
/* Just because a person is a SYSADMIN doesn't necessarily mean he/she
* knows everything about installing software. In fact, many have been
* known to install the talk daemon without setting the option required
* to pad out the structures so that "long"s are on four-byte boundaries
* on machines where "long"s can be on two-byte boundaries. This "bug"
* cost me about four hours of debugging to discover, so I'm not happy
* right now. Anyway, here's a quick hack to fix this problem.
*/
if(d == otalk && nrsp.type == LOOK_UP && nrsp.answer == 0)
{
u_short t;
(void)memcpy((char *)&t, ((char *)&orsp.addr.sin_family)-2, sizeof(t));
if(ntohs(t) == AF_INET && ntohs(orsp.addr.sin_family) != AF_INET)
{
char *c;
c = ((char *)&orsp) + sizeof(orsp) - 1;
for(; c >= (char *)&orsp.id_num; c--)
*c = *(c-2);
}
}
/* Fill in the new talk response structure if we just read an
* old one.
*/
if(d == otalk)
{
nrsp.type = orsp.type;
nrsp.answer = orsp.answer;
nrsp.id_num = orsp.id_num;
nrsp.addr = orsp.addr;
}
return 0;
}
/* find_daemon() locates the talk daemon(s) on a machine and determines
* what version(s) of the daemon are running.
*/
static int
find_daemon(addr)
u_long addr;
{
register hostinfo *h;
register int n, i, d;
CTL_MSG m1;
CTL_MSG42 m2;
struct sockaddr_in daemon;
struct timeval tv;
int sel, out;
static hostinfo *host_head = NULL;
/* If we've already used this host, look it up instead of blitting to
* the daemons again...
*/
for(h = host_head; h; h = h->next)
if(h->host_addr == addr)
return h->dtype;
daemon.sin_family = AF_INET;
IN_ADDR(daemon) = addr;
m1 = omsg;
m2 = nmsg;
m1.ctl_addr = talkd[otalk].sock;
place_my_address(&(m1.ctl_addr), addr);
m2.ctl_addr = talkd[ntalk].sock;
place_my_address(&(m2.ctl_addr), addr);
m1.type = m2.type = LOOK_UP;
m1.id_num = m2.id_num = htonl(0);
m1.r_tty[0] = m2.r_tty[0] = '\0';
strcpy(m1.r_name, "ytalk");
strcpy(m2.r_name, "ytalk");
m1.addr.sin_family = m2.addr.sin_family = htons(AF_INET);
out = 0;
for(i = 0; i < 5; i++)
{
IN_PORT(daemon) = talkd[ntalk].port;
n = sendto(talkd[ntalk].fd, &m2, sizeof(m2),
0, (struct sockaddr *) &daemon, sizeof(daemon));
if(n != sizeof(m2))
show_error("Warning: cannot write to new talk daemon");
IN_PORT(daemon) = talkd[otalk].port;
n = sendto(talkd[otalk].fd, &m1, sizeof(m1),
0, (struct sockaddr *) &daemon, sizeof(daemon));
if(n != sizeof(m1))
show_error("Warning: cannot write to old talk daemon");
tv.tv_sec = 4L;
tv.tv_usec = 0L;
sel = (1 << talkd[ntalk].fd) | (1 << talkd[otalk].fd);
if((n = select(32, &sel, 0, 0, &tv)) < 0)
{
show_error("find_daemon: first select() failed");
continue;
}
if(n == 0)
continue;
do
{
for(d = 1; d <= daemons; d++)
if(sel & (1 << talkd[d].fd))
{
out |= (1 << d);
if(recv(talkd[d].fd, errstr, talkd[d].rlen, 0) < 0)
show_error("find_daemon: recv() failed");
}
tv.tv_sec = 0L;
tv.tv_usec = 500000L; /* give the other daemon a chance */
sel = (1 << talkd[ntalk].fd) | (1 << talkd[otalk].fd);
if((n = select(32, &sel, 0, 0, &tv)) < 0)
show_error("find_daemon: second select() failed");
} while(n > 0);
h = (hostinfo *)get_mem(sizeof(hostinfo));
h->next = host_head;
host_head = h;
h->host_addr = addr;
h->dtype = out;
return out;
}
sprintf(errstr, "No talk daemon on %s", host_name(addr));
show_error(errstr);
return 0;
}
static u_long
make_net_mask(addr)
u_long addr;
{
if(addr & (u_long)0xff)
return (u_long)0xffffffff;
if(addr & (u_long)0xffff)
return (u_long)0xffffff00;
if(addr & (u_long)0xffffff)
return (u_long)0xffff0000;
if(addr)
return (u_long)0xff000000;
return (u_long)0;
}
/* ---- global functions ---- */
/* Initialize sockets and message parameters.
*/
void
init_socket()
{
/* init daemons in order of preference */
ntalk = init_daemon("ntalk", 518, &nmsg, sizeof(nmsg),
&nrsp, sizeof(nrsp));
otalk = init_daemon("talk", 517, &omsg, sizeof(omsg),
&orsp, sizeof(orsp));
strncpy(nmsg.l_name, me->user_name, NAME_SIZE);
omsg.ctl_addr = talkd[otalk].sock;
nmsg.ctl_addr = talkd[ntalk].sock;
nmsg.vers = TALK_VERSION;
(void)find_daemon(me->host_addr);
init_autoport();
}
/* Close every open descriptor. This should only be used for a quick
* exit... it does not gracefully shut systems down.
*/
void
close_all()
{
register yuser *u;
register int d;
for(u = user_list; u; u = u->unext)
{
if(u->fd > 0)
close(u->fd);
if(u->output_fd > 0)
close(u->output_fd);
}
if(autofd > 0)
close(autofd);
for(d = 1; d <= daemons; d++)
close(talkd[d].fd);
}
/* The following routines send a request across the DGRAM socket to the
* talk daemons.
*/
/* First, a quick and easy interface for the user sockets.
*/
int
send_dgram(user, type)
yuser *user;
u_char type;
{
u_long addr;
int d;
/* set up the message type and where to send it */
switch(type)
{
case LEAVE_INVITE: /* leave an invite on my machine */
addr = me->host_addr;
nmsg.type = LEAVE_INVITE;
nmsg.id_num = htonl(user->l_id);
break;
case DELETE_INVITE: /* delete my invite on my machine */
addr = me->host_addr;
nmsg.type = DELETE;
nmsg.id_num = htonl(user->l_id);
break;
case ANNOUNCE: /* ring a user */
addr = user->host_addr;
nmsg.type = ANNOUNCE;
announce_id += 5; /* no guesswork here */
nmsg.id_num = htonl(announce_id);
break;
case LOOK_UP: /* look up remote invitation */
addr = user->host_addr;
nmsg.type = LOOK_UP;
nmsg.id_num = htonl(user->r_id);
break;
case DELETE: /* delete erroneous remote invitation */
addr = user->host_addr;
nmsg.type = DELETE;
nmsg.id_num = htonl(user->r_id);
break;
case AUTO_LOOK_UP: /* look up remote auto-invitation */
addr = user->host_addr;
nmsg.type = LOOK_UP;
nmsg.id_num = htonl(user->r_id);
break;
case AUTO_DELETE: /* delete erroneous remote auto-invitation */
addr = user->host_addr;
nmsg.type = DELETE;
nmsg.id_num = htonl(user->r_id);
break;
default:
errno = 0;
show_error("send_dgram: unknown type");
return -1;
}
/* find a common daemon, if possible */
if(user->daemon != 0)
d = user->daemon;
else
{
int dtype, d1, d2;
/* Find the daemon(s) their host supports. If our two machines
* support a daemon in common, use that one. Else, normal UNIX
* "talk" is already screwed to the wall, but YTalk will at least
* work.
*/
d1 = find_daemon(user->host_addr);
d2 = find_daemon(me->host_addr);
dtype = d1 & d2;
if(d1 == 0 || d2 == 0)
return -1;
if(dtype == 0)
{
dtype = find_daemon(addr);
for(d = 1; d <= daemons; d++)
if(dtype & (1<<d))
break;
if(d > daemons)
return -1;
}
else
{
for(d = 1; d <= daemons; d++)
if(dtype & (1<<d))
{
user->daemon = d;
break;
}
if(d > daemons)
return -1;
}
}
/* Each user has his own unique daemon id. Why? Tsch. Why.
* Well, the talk daemons consider two users equivalent if their
* usernames and machine names match. Hence, the daemons will not
* allow ytalk to talk with two different users with the same name
* on some machine. By assigning unique process id's, we trick
* the daemons into thinking we're several different users trying
* to talk to the same person. Sick? Don't blame me.
*/
nmsg.pid = htonl(user->d_id);
if(type == AUTO_LOOK_UP || type == AUTO_DELETE)
{
strcpy(nmsg.l_name, "+AUTO"); /* put on my mask... */
strncpy(nmsg.r_name, user->user_name, NAME_SIZE);
nmsg.r_tty[0] = '\0';
}
else
{
strncpy(nmsg.r_name, user->user_name, NAME_SIZE);
strncpy(nmsg.r_tty, user->tty_name, TTY_SIZE);
}
nmsg.addr = user->sock;
nmsg.addr.sin_family = htons(AF_INET);
if(sendit(addr, d) != 0)
return -2;
switch(type)
{
case LEAVE_INVITE:
user->l_id = ntohl(nrsp.id_num);
break;
case LOOK_UP:
user->r_id = ntohl(nrsp.id_num);
break;
case AUTO_LOOK_UP:
strncpy(nmsg.l_name, me->user_name, NAME_SIZE);
user->r_id = ntohl(nrsp.id_num);
break;
case AUTO_DELETE:
strncpy(nmsg.l_name, me->user_name, NAME_SIZE);
break;
}
return nrsp.answer;
}
/* Next, an interface for the auto-invite socket. The auto-invite socket
* always sends to the caller's host, and always does just an invite.
*/
int
send_auto(type)
u_char type;
{
int dtype, d, rc;
if(autofd < 0)
return 0;
nmsg.type = type;
strcpy(nmsg.r_name, "+AUTO");
nmsg.r_tty[0] = '\0';
nmsg.addr = autosock;
nmsg.addr.sin_family = htons(AF_INET);
rc = 0;
dtype = find_daemon(me->host_addr);
for(d = daemons; d >= 1; d--)
if(dtype & (1<<d))
{
nmsg.id_num = htonl(autoid[d]);
nmsg.pid = htonl(1);
if(sendit(me->host_addr, d) < 0)
rc = -1;
else
autoid[d] = ntohl(nrsp.id_num);
}
if(rc)
return rc;
if(type == LEAVE_INVITE)
return 0;
return nrsp.answer;
}
/* Shut down the auto-invitation system.
*/
void
kill_auto()
{
if(autofd < 0)
return;
(void)send_auto(DELETE);
remove_fd(autofd);
close(autofd);
autofd = -1;
}
/* Create a TCP socket for communication with other talk users.
*/
int
newsock(user)
yuser *user;
{
int socklen, fd;
user->sock.sin_family = AF_INET;
IN_ADDR(user->sock) = INADDR_ANY;
IN_PORT(user->sock) = 0;
if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
show_error("newsock: socket() failed");
return -1;
}
if(bind(fd, (struct sockaddr *)&user->sock, sizeof(struct sockaddr_in)) < 0)
{
close(fd);
show_error("newsock: bind() failed");
return -1;
}
socklen = sizeof(struct sockaddr_in);
if(getsockname(fd, (struct sockaddr *)&user->sock, &socklen) < 0)
{
close(fd);
show_error("newsock: getsockname() failed");
return -1;
}
place_my_address(&(user->sock), user->host_addr);
if(listen(fd, 5) < 0)
{
close(fd);
show_error("newsock: listen() failed");
return -1;
}
user->fd = fd;
fd_to_user[user->fd] = user;
user->orig_sock = user->sock;
return 0;
}
/* Connect to another user's communication socket.
*/
int
connect_to(user)
yuser *user;
{
register yuser *u;
int socklen, fd;
struct sockaddr_in sock, orig_sock;
orig_sock = *(struct sockaddr_in *)&nrsp.addr;
orig_sock.sin_family = AF_INET;
/* it could be one of mine... */
for(u = user_list; u; u = u->unext)
if(SOCK_EQUAL(orig_sock, u->orig_sock))
return -3;
if(SOCK_EQUAL(orig_sock, autosock))
return -3;
sock = orig_sock;
if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
show_error("connect_to: socket() failed");
return -1;
}
if(connect(fd, (struct sockaddr *)&sock, sizeof(struct sockaddr_in)) < 0)
{
close(fd);
if(errno == ECONNREFUSED)
{
errno = 0;
return -2;
}
show_error("connect_to: connect() failed");
return -1;
}
socklen = sizeof(struct sockaddr_in);
if(getsockname(fd, (struct sockaddr *)&sock, &socklen) < 0)
{
close(fd);
show_error("connect_to: getsockname() failed");
return -1;
}
if(user)
{
user->sock = sock;
user->orig_sock = orig_sock;
user->fd = fd;
fd_to_user[user->fd] = user;
}
return fd;
}
/* Find a host's address.
*/
u_long
get_host_addr(hostname)
char *hostname;
{
struct hostent *host;
u_long addr;
u_long inet_addr();
if((host = (struct hostent *) gethostbyname(hostname)) != NULL)
{
if(host->h_length != sizeof(addr))
{
sprintf(errstr, "Bad IN addr: %s\n", hostname);
show_error(errstr);
return (u_long)-1;
}
(void)memcpy(&addr, host->h_addr, sizeof(addr));
}
else if((addr = inet_addr(hostname)) == (u_long)-1)
return (u_long)-1;
return addr;
}
/* Find a host name by host address.
*/
char *
host_name(addr)
u_long addr;
{
struct hostent *host;
char *inet_ntoa();
if((host = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET)) == NULL)
{
struct in_addr tmp;
tmp.s_addr = addr;
return inet_ntoa(tmp);
}
return host->h_name;
}
/* Re-address a given host ("from_id") to the given address or host
* id ("to_id") when communicating with some other host id ("on_id").
* This is useful especially over routers where "foo.com" is known
* as the differently-addressed "bar.com" to host "xyzzy.com".
*/
void
readdress_host(from_id, to_id, on_id)
char *from_id, *to_id, *on_id;
{
register readdr *new;
u_long from_addr, to_addr, on_addr;
u_long from_mask, to_mask, on_mask;
if((from_addr = get_host_addr(from_id)) == (u_long)-1)
{
sprintf(errstr, "Unknown host: '%s'\n", from_id);
show_error(errstr);
return;
}
if((to_addr = get_host_addr(to_id)) == (u_long)-1)
{
sprintf(errstr, "Unknown host: '%s'\n", to_id);
show_error(errstr);
return;
}
if((on_addr = get_host_addr(on_id)) == (u_long)-1)
{
sprintf(errstr, "Unknown host: '%s'\n", on_id);
show_error(errstr);
return;
}
from_mask = make_net_mask(from_addr);
to_mask = make_net_mask(to_addr);
on_mask = make_net_mask(on_addr);
if((from_addr & from_mask) != (me->host_addr & from_mask))
return;
if(from_addr == to_addr)
return;
new = (readdr *)get_mem(sizeof(readdr));
new->addr = on_addr;
new->mask = on_mask;
new->id_addr = to_addr;
new->id_mask = to_mask;
new->next = readdr_list;
readdr_list = new;
}