Exemple #1
0
static unsigned long
realaddr(register Cs_t* state, const char* name)
{
	register int		n;
	register const char*	s;
	int			r;
	const char*		t;
	union nl		addr;

#if CS_LIB_SOCKET

	register struct hostent*	hp;

#endif
	messagef((state->id, NiL, -8, "realaddr(%s) call", name));
	state->flags &= ~CS_ADDR_NUMERIC;
	s = name;
	if (streq(s, CS_HOST_LOCAL))
	{
		messagef((state->id, NiL, -8, "realaddr(%s) = %s, flags = |%s%s%s", name, csntoa(state, local), (state->flags & CS_ADDR_LOCAL) ? "LOCAL|" : "", (state->flags & CS_ADDR_REMOTE) ? "REMOTE|" : "", (state->flags & CS_ADDR_SHARE) ? "SHARE|" : ""));
		return local;
	}

	/*
	 * check for 0xX.X.X.X or n.n.n.n
	 */

	if (*s == '0' && *(s + 1) == 'x')
	{
		s += 2;
		r = 16;
	}
	else r = 0;
	addr.l = 0;
	n = 0;
	do
	{
		addr.c[n] = strtol(s, (char**)&t, r) & 0xff;
		if (t == s) break;
		s = t;
	} while (++n < 4 && *s && *s++ == '.');
	if (!*s && n == 4)
	{
		if (!addr.l || addr.c[0] == 127 && addr.c[1] == 0 && addr.c[2] == 0 && addr.c[3] <= 1)
		{
			addr.l = csaddr(state, NiL);
			if (local == CS_LOCAL)
			{
				addr.c[0] = 127;
				addr.c[1] = 0;
				addr.c[2] = 0;
				addr.c[3] = 1;
				local = addr.l;
			}
			else
				addr.l = local;
		}
		state->flags |= CS_ADDR_NUMERIC;
		messagef((state->id, NiL, -8, "realaddr(%s) = %s, flags = |%s%s%s", name, csntoa(state, addr.l), (state->flags & CS_ADDR_LOCAL) ? "LOCAL|" : "", (state->flags & CS_ADDR_REMOTE) ? "REMOTE|" : "", (state->flags & CS_ADDR_SHARE) ? "SHARE|" : ""));
		return addr.l;
	}

	/*
	 * local conversion
	 */

	csdb(state);

#if CS_LIB_V10 || CS_LIB_SOCKET

#if CS_LIB_V10
	if ((addr.l = (unsigned long)in_address(name)) == INADDR_ANY)
		addr.l = 0;
#else
	addr.l = ((hp = gethostbyname(name)) && hp->h_addrtype == AF_INET && hp->h_length <= sizeof(struct in_addr)) ? (unsigned long)((struct in_addr*)hp->h_addr)->s_addr : 0;
#endif
	if (addr.c[0] == 127 && addr.c[1] == 0 && addr.c[2] == 0 && addr.c[3] <= 1)
	{
		if (local == CS_LOCAL)
			local = addr.l;
		else
			addr.l = local;
	}
	messagef((state->id, NiL, -8, "realaddr(%s) = %s, flags = |%s%s%s", name, csntoa(state, addr.l), (state->flags & CS_ADDR_LOCAL) ? "LOCAL|" : "", (state->flags & CS_ADDR_REMOTE) ? "REMOTE|" : "", (state->flags & CS_ADDR_SHARE) ? "SHARE|" : ""));
#else
	messagef((state->id, NiL, -8, "realaddr(%s) not found", name));
	addr.l = 0;
#endif
	return addr.l;
}
Exemple #2
0
int
csopen(register Cs_t* state, const char* apath, int op)
{
	register char*	path = (char*)apath;
	register char*	b;
	register char*	s;
	register int	n;
	int		fd;
	char*		t;
	char*		u;
	char*		type;
	char*		endtype;
	char*		host;
	char*		endhost;
	char*		serv;
	char*		endserv;
	char*		qual;
	char*		endqual;
	char*		opath;
	char*		user = 0;
	char*		group = 0;
	char*		trust = 0;
	char*		arg = 0;
	int		nfd = -1;
	int		uid = -1;
	int		gid = -1;
	int		sid = -1;
	int		auth = 1;
	int		mode;
	unsigned long	addr;
	unsigned long	port = 0;
	struct stat	st;
	char		buf[PATH_MAX];
	char		tmp[PATH_MAX];

	if (!path)
	{
		errno = EFAULT;
		return -1;
	}
	csprotect(&cs);
	if (op < 0)
		op = CS_OPEN_TEST;
	messagef((state->id, NiL, -8, "open(%s,%o) call", path, op));

	/*
	 * blast out the parts
	 */

	opath = path;
	if (pathgetlink(path, buf, sizeof(buf)) <= 0)
	{
		if (strlen(path) >= sizeof(buf))
			return -1;
		strcpy(buf, path);
	}
	else if ((state->flags & CS_ADDR_LOCAL) && (s = strrchr(buf, '/')))
	{
		/*
		 * dynamic ip assignment can change the addr
		 * underfoot in some implementations so we
		 * double check the local ip here
		 */

		strcpy(tmp, buf);
		if (tokscan(tmp, NiL, "/dev/%s/%s/%s", &type, NiL, &serv) == 3)
			sfsprintf(buf, sizeof(buf), "/dev/%s/%s/%s", type, csntoa(state, 0), serv);
	}
	path = buf;
	pathcanon(path, 0, 0);
	errno = ENOENT;
	strcpy(state->path, path);
	b = path;
	if ((*b++ != '/') || !(s = strchr(b, '/')))
		return -1;
	*s++ = 0;
	if (!streq(b, "dev"))
		return -1;
	if (b = strchr(s, '/'))
		*b++ = 0;
	if (streq(s, "fdp"))
	{
#if !( CS_LIB_SOCKET_UN || CS_LIB_STREAM || CS_LIB_V10 )
		if (access(CS_PROC_FD_TST, F_OK))
		{
			errno = ENODEV;
			messagef((state->id, NiL, -1, "open: %s: %s: not supported", state->path, s));
			return -1;
		}
#endif
	}
	else if (!streq(s, "tcp") && !streq(s, "udp"))
	{
		messagef((state->id, NiL, -1, "open: %s: %s: invalid type", state->path, s));
		return -1;
	}
#if !( CS_LIB_SOCKET || CS_LIB_STREAM || CS_LIB_V10 )
	else
	{
		errno = ENODEV;
		messagef((state->id, NiL, -1, "open: %s: %s: not supported", state->path, s));
		return -1;
	}
#endif
	type = s;
	qual = state->qual;
	if (!b)
		host = serv = 0;
	else
	{
		host = b;
		if (!(s = strchr(b, '/')))
			serv = 0;
		else
		{
			*s++ = 0;
			serv = s;

			/*
			 * grab the next fd to preserve open semantics
			 */

			for (n = 0; n < 10; n++)
				if ((nfd = dup(n)) >= 0)
					break;

			/*
			 * get qual, perm and arg
			 */

			mode = S_IRWXU|S_IRWXG|S_IRWXO;
			if (b = strchr(s, '/'))
			{
				*b++ = 0;
				do
				{
					if (*b == '#')
					{
						arg = b + 1;
						break;
					}
					if (u = strchr(b, '/'))
						*u++ = 0;
					if (s = strchr(b, '='))
						*s++ = 0;
					for (n = 0, t = b; *t; n = HASHKEYPART(n, *t++));
					switch (n)
					{
					case HASHKEY5('g','r','o','u','p'):
						group = s ? s : "";
						break;
					case HASHKEY5('l','o','c','a','l'):
						op |= CS_OPEN_LOCAL;
						break;
					case HASHKEY3('n','o','w'):
						op |= CS_OPEN_NOW;
						break;
					case HASHKEY5('o','t','h','e','r'):
						auth = 0;
						break;
					case HASHKEY6('r','e','m','o','t','e'):
						op |= CS_OPEN_REMOTE;
						break;
					case HASHKEY5('s','h','a','r','e'):
						op |= CS_OPEN_SHARE;
						break;
					case HASHKEY5('s','l','a','v','e'):
						op |= CS_OPEN_SLAVE;
						break;
					case HASHKEY4('t','e','s','t'):
						op |= CS_OPEN_TEST;
						break;
					case HASHKEY5('t','r','u','s','t'):
						op |= CS_OPEN_TRUST;
						trust = s;
						break;
					case HASHKEY4('u','s','e','r'):
						user = s ? s : "";
						break;
					default:
						qual += sfsprintf(qual, sizeof(state->qual) - (qual - state->qual) - 1, "%s%s", qual == state->qual ? "" : "-", b);
						if (s)
							*(s - 1) = '=';
						break;
					}
				} while (b = u);
			}
		}
	}
	if (*type != 't')
		auth = 0;
	strncpy(state->type, type, sizeof(state->type) - 1);
	qual = (qual == state->qual) ? (char*)0 : state->qual;
	messagef((state->id, NiL, -8, "open: type=%s host=%s serv=%s qual=%s", type, host, serv, qual));
	if (host)
	{
		/*
		 * validate host
		 */

		if (!(state->addr = addr = csaddr(state, host)))
		{
			if (serv && !(op & CS_OPEN_CREATE) && *type == 't' && (port = csport(state, type, serv)) >= CS_PORT_MIN && port <= CS_PORT_MAX)
			{
				/*
				 * attempt proxy connection
				 */

				if (nfd >= 0)
				{
					close(nfd);
					nfd = -1;
				}
				if ((fd = state->proxy.addr ? csbind(state, type, state->proxy.addr, state->proxy.port, 0L) : reopen(state, csvar(state, CS_VAR_PROXY, 0))) >= 0)
				{
					state->proxy.addr = state->addr;
					state->proxy.port = state->port;
					n = sfsprintf(tmp, sizeof(tmp), "\n%s!%s!%d\n\n%s\n%s\n0\n-1\n-1\n", type, host, port, csname(state, 0), error_info.id ? error_info.id : state->id);
					if (cswrite(state, fd, tmp, n) == n && (n = csread(state, fd, tmp, sizeof(tmp), CS_LINE)) >= 2)
					{
						if (tmp[0] == '0' && tmp[1] == '\n')
							return fd;
						if (error_info.trace <= -4 && n > 2)
						{
							s = tmp;
							s[n - 1] = 0;
							while (*s && *s++ != '\n');
							messagef((state->id, NiL, -4, "%s error message `%s'", csvar(state, CS_VAR_PROXY, 0), s));
						}
					}
					close(fd);
				}
			}
#ifdef EADDRNOTAVAIL
			errno = EADDRNOTAVAIL;
#else
			errno = ENOENT;
#endif
			goto bad;
		}
		if (op & CS_OPEN_LOCAL)
		{
			state->flags |= CS_ADDR_LOCAL;
			state->flags &= ~CS_ADDR_REMOTE;
		}
		if (op & CS_OPEN_NOW)
			state->flags |= CS_ADDR_NOW;
		if ((op & (CS_OPEN_AGENT|CS_OPEN_REMOTE)) == CS_OPEN_REMOTE)
		{
			state->flags |= CS_ADDR_REMOTE;
			state->flags &= ~CS_ADDR_LOCAL;
		}
		if (op & CS_OPEN_SHARE)
			state->flags |= CS_ADDR_SHARE;
		if (op & CS_OPEN_SLAVE)
			state->flags |= CS_DAEMON_SLAVE;
		if (op & CS_OPEN_TEST)
			state->flags |= CS_ADDR_TEST;
		if (op & CS_OPEN_TRUST)
			state->flags |= CS_ADDR_TRUST;
		if ((state->flags & CS_ADDR_REMOTE) && (!serv || !strneq(serv, CS_SVC_INET, sizeof(CS_SVC_INET) - 1) && (strtol(serv, &t, 0), *t)))
			return agent(state, state->host, state->user, state->path);
		if (s = user)
		{
			n = geteuid();
			if (*s)
			{
				if ((uid = struid(s)) < 0)
				{
					uid = strtol(s, &t, 0);
					if (*t)
					{
						errno = EACCES;
						goto bad;
					}
				}
				if (n && uid != n)
				{
					errno = EACCES;
					goto bad;
				}
			}
			else
				uid = n;
			mode &= ~(S_IRWXG|S_IRWXO);
		}
		if (s = group)
		{
			n = getegid();
			if (*s)
			{
				if ((gid = strgid(s)) < 0)
				{
					gid = strtol(s, &t, 0);
					if (*t)
					{
						errno = EACCES;
						goto bad;
					}
				}
				if (geteuid() && gid != n)
				{
					gid_t*	groups;
					int	g;

					if ((g = getgroups(0, NiL)) <= 0)
						g = getconf("NGROUPS_MAX");
					if (groups = newof(0, gid_t, g, 0))
					{
						for (n = getgroups(g, groups); n >= 0; n--)
							if (gid == groups[n])
								break;
						free(groups);
					}
					else
						n = -1;
					if (n < 0)
					{
						errno = EACCES;
						goto bad;
					}
				}
			}
			else
				gid = n;
			mode &= ~S_IRWXO;
		}
		if (s = trust)
		{
			if (!*s)
				sid = geteuid();
			else if ((sid = struid(s)) < 0)
			{
				sid = strtol(s, &t, 0);
				if (*t)
				{
					errno = EACCES;
					goto bad;
				}
			}
		}
		if (state->flags & CS_ADDR_SHARE)
			host = CS_HOST_SHARE;
		else
		{
			host = state->host;
			if (!(state->flags & CS_ADDR_LOCAL))
			{
				if (*type == 'f')
				{
					errno = ENODEV;
					goto bad;
				}
				if (op & CS_OPEN_CREATE)
				{
					errno = EROFS;
					goto bad;
				}
			}
			if (serv && !qual && *type != 'f' && (port = csport(state, type, serv)) != CS_PORT_INVALID)
			{
				if (op & CS_OPEN_CREATE)
					addr = 0;
				else if (port == CS_PORT_RESERVED || port == CS_PORT_NORMAL)
					goto bad;
				if (nfd >= 0)
				{
					close(nfd);
					nfd = -1;
				}
				state->control = 0;
				if ((fd = csbind(state, type, addr, port, 0L)) >= 0)
				{
					if (mode != (S_IRWXU|S_IRWXG|S_IRWXO) && csauth(state, fd, NiL, NiL))
					{
						close(fd);
						return -1;
					}
					return fd;
				}
			}
		}
	}

	/*
	 * get the mount dir prefix
	 */

	if (opath == (b = path = state->mount))
	{
#ifdef ELOOP
		errno = ELOOP;
#else
		errno = EINVAL;
#endif
		goto bad;
	}
	if (*type == 'f')
	{
		if (host && !(state->flags & CS_ADDR_LOCAL))
		{
			errno = ENODEV;
			goto bad;
		}
		b += sfsprintf(b, sizeof(state->mount) - (b - path), "%s", csvar(state, CS_VAR_LOCAL, 0));
		if ((op & CS_OPEN_CREATE) && eaccess(path, X_OK) && (mkdir(path, S_IRWXU|S_IRWXG|S_IRWXO) || chmod(path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)))
			goto bad;
	}
	else
	{
		if (op & CS_OPEN_TRUST)
		{
			if (!pathaccess(csvar(state, CS_VAR_TRUST, 1), csvar(state, CS_VAR_SHARE, 1), NiL, PATH_EXECUTE, b, sizeof(state->mount) - (b - state->mount)))
				goto bad;
		}
		else if (!pathpath(csvar(state, CS_VAR_SHARE, 0), "", PATH_EXECUTE, b, sizeof(state->mount) - (b - state->mount)))
			goto bad;
		b += strlen(b);
	}

	/*
	 * add the type
	 */

	b += sfsprintf(b, sizeof(state->mount) - (b - path), "/%s", type);
	if (!host)
	{
		*(state->control = b + 1) = 0;
		if (nfd >= 0)
			close(nfd);
		if ((fd = open(path, O_RDONLY)) < 0)
		{
			mkmount(state, S_IRWXU|S_IRWXG|S_IRWXO, -1, -1, NiL, NiL, NiL);
			fd = open(path, O_RDONLY);
		}
		if (fd < 0)
			messagef((state->id, NiL, -1, "open: %s: %s: open error", state->path, path));
		return fd;
	}
	endtype = b;

	/*
	 * add the host
	 */

	if (strlen(host) <= CS_MNT_MAX)
		b += sfsprintf(b, sizeof(state->mount) - (b - path), "/%s", host);
	else
	{
		s = csntoa(state, addr);
		if (strlen(s) <= CS_MNT_MAX)
			b += sfsprintf(b, sizeof(state->mount) - (b - path), "/%s", s);
		else
		{
			unsigned char*	a = (unsigned char*)&addr;

			b += sfsprintf(b, sizeof(state->mount) - (b - path), "/0x%X.%X.%X.%X", a[0], a[1], a[2], a[3]);
		}
	}
	messagef((state->id, NiL, -8, "%s:%d host=`%s' path=`%s'", __FILE__, __LINE__, host, path));
	if (!serv)
	{
		*(state->control = b + 1) = 0;
		if (nfd >= 0)
			close(nfd);
		if ((fd = open(path, O_RDONLY)) < 0)
			messagef((state->id, NiL, -1, "open: %s: %s: open error", state->path, path));
		return fd;
	}
	endhost = b;

	/*
	 * add the service
	 */

	sfsprintf(b, sizeof(state->mount) - (b - path), "%s/%s/%s/%s%s", CS_SVC_DIR, type, serv, serv, CS_SVC_SUFFIX);
	if (!pathpath(b, "", PATH_ABSOLUTE|PATH_EXECUTE, tmp, sizeof(tmp)) || stat(tmp, &st))
		op |= CS_OPEN_TEST;
	else
	{
		*strrchr(tmp, '/') = 0;
		if (!(op & CS_OPEN_TRUST))
			sid = st.st_uid;
		if (!st.st_size)
			op |= CS_OPEN_TEST;
	}
	b += sfsprintf(b, sizeof(state->mount) - (b - path), "/%s", serv);
	endserv = b;

	/*
	 * add the qualifier and perm
	 */

	if (sid >= 0)
		b += sfsprintf(b, sizeof(state->mount) - (b - path), "/%d-", sid);
	else
		b += sfsprintf(b, sizeof(state->mount) - (b - path), "/-");
	if (uid >= 0)
		b += sfsprintf(b, sizeof(state->mount) - (b - path), "%d-", uid);
	else if (gid >= 0)
		b += sfsprintf(b, sizeof(state->mount) - (b - path), "-%d", gid);
	else
		b += sfsprintf(b, sizeof(state->mount) - (b - path), "-");
#if limit_qualifier_length
	endqual = endserv + CS_MNT_MAX + 1;
#else
	endqual = state->mount + sizeof(state->mount) - 1;
#endif
	if (qual)
	{
		if (b < endqual)
			*b++ = '-';
		while (b < endqual && *qual)
			*b++ = *qual++;
	}
	if (*type == 't' && !auth)
	{
		if (b >= endqual)
			b--;
		*b++ = CS_MNT_OTHER;
	}

	/*
	 * add in the connect stream control
	 */

	*b++ = '/';
	*b = CS_MNT_STREAM;
	strcpy(b + 1, CS_MNT_TAIL);
	messagef((state->id, NiL, -8, "%s:%d %s", __FILE__, __LINE__, state->mount));
	state->control = b;

	/*
	 * create the mount subdirs if necessary
	 */

	if ((op & CS_OPEN_CREATE) && mkmount(state, mode, uid, gid, endserv, endhost, endtype))
		goto bad;
	mode &= S_IRWXU|S_IRWXG|S_IRWXO;
	if (nfd >= 0)
	{
		close(nfd);
		nfd = -1;
	}
	if (op & CS_OPEN_MOUNT)
	{
		messagef((state->id, NiL, -1, "open(%s,%o) = %d, mount = %s", state->path, op, state->mount));
		return 0;
	}
	if (*type == 'f')
	{
		/*
		 * {fdp}
		 */

		if ((fd = doattach(state, path, op, mode, user, opath, tmp, serv, b)) < 0)
			return -1;
	}
	else
	{
		/*
		 * {tcp,udp}
		 */

		messagef((state->id, NiL, -8, "%s:%d %s", __FILE__, __LINE__, state->mount));
		if ((fd = reopen(state, path)) < 0)
		{
			/*
			 * check for old single char cs mount
			 */

			*(state->control + 1) = 0;
			if ((fd = reopen(state, path)) < 0)
				messagef((state->id, NiL, -1, "open: %s: %s: reopen error", state->path, path));
			*(state->control + 1) = CS_MNT_TAIL[0];
		}
		if (op & CS_OPEN_CREATE)
		{
			if (fd >= 0)
			{
				close(fd);
				errno = EEXIST;
				return -1;
			}
			if (errno != ENOENT && errno != ENOTDIR)
				return -1;
			sigcritical(1);
			*state->control = CS_MNT_LOCK;
			if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0)) < 0)
			{
				if (stat(path, &st))
				{
					messagef((state->id, NiL, -1, "open: %s: %s: creat error", state->path, path));
					goto unblock;
				}
				if ((CSTIME() - (unsigned long)st.st_ctime) < 2 * 60)
				{
					errno = EEXIST;
					messagef((state->id, NiL, -1, "open: %s: %s: another server won the race", state->path, path));
					goto unblock;
				}
				if (remove(path))
				{
					messagef((state->id, NiL, -1, "open: %s: %s: remove error", state->path, path));
					goto unblock;
				}
				if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0)) < 0)
				{
					messagef((state->id, NiL, -1, "open: %s: %s: creat error", state->path, path));
					goto unblock;
				}
			}
			close(fd);
			if (!port && (n = strtol(serv, &t, 0)) && t > serv && !*t)
				port = n;
			else if (geteuid())
				port = CS_NORMAL;
			else
				port = CS_RESERVED;
			if ((fd = csbind(state, type, 0L, port, 0L)) >= 0)
			{
				*state->control = CS_MNT_STREAM;
				remove(path);
				if (pathsetlink(cspath(state, fd, 0), path))
				{
					messagef((state->id, NiL, -1, "open: %s: %s: link error", cspath(state, fd, 0), path));
					close(fd);
					fd = -1;
				}
			}
		unblock:
			*state->control = CS_MNT_LOCK;
			remove(path);
			sigcritical(0);
			*state->control = CS_MNT_STREAM;
			if (fd < 0)
				return -1;
		}
		else if (fd < 0 && ((op & CS_OPEN_TEST) || initiate(state, user, opath, tmp, serv) || (fd = reopen(state, path)) < 0))
		{
			messagef((state->id, NiL, -1, "open: %s: %s: reopen/initiate error", state->path, path));
			return -1;
		}
		else if (!(op & CS_OPEN_AGENT))
		{
			*state->control = CS_MNT_AUTH;
			n = csauth(state, fd, path, arg);
			*state->control = CS_MNT_STREAM;
			if (n)
			{
				close(fd);
				messagef((state->id, NiL, -1, "open: %s: %s: authentication error", state->path, path));
				return -1;
			}
		}
	}

	/*
	 * fd is open at this point
	 * make sure its not a bogus mount
	 */

	if (mode != (S_IRWXU|S_IRWXG|S_IRWXO))
	{
		*state->control = 0;
		n = stat(path, &st);
		*state->control = CS_MNT_STREAM;
		if (n)
		{
			messagef((state->id, NiL, -1, "open: %s: %s: stat error", state->path, path));
			close(fd);
			return -1;
		}
		if (uid >= 0 && st.st_uid != uid || gid >= 0 && st.st_gid != gid)
		{
			close(fd);
			errno = EPERM;
			messagef((state->id, NiL, -1, "open: %s: %s: uid/gid error", state->path, path));
			return -1;
		}
	}
	return fd;
 bad:
	if (nfd >= 0)
		close(nfd);
	return -1;
}
Exemple #3
0
unsigned long
csaddr(register Cs_t* state, const char* aname)
{
	register char*		name = (char*)aname;
	register char*		s;
	register unsigned long	addr;
	register Sfio_t*	sp = 0;
	int			userlen = 0;
	int			dot = 0;
	long			flags = 0;
	char*			user;

	messagef((state->id, NiL, -8, "addr(%s) call", name));
	if (!local)
	{
#if CS_LIB_SOCKET
		if (!state->db)
			state->db = -1;
#endif
		local = CS_LOCAL;
		if (addr = realaddr(state, csname(state, 0)))
			local = addr;
	}
	if (!name)
	{
		addr = local;
		goto ok;
	}
	if (s = strchr(name, '@'))
	{
		userlen = s - name;
		user = name;
		name = s + 1;
	}
	if (strneq(name, CS_HOST_SHARE, sizeof(CS_HOST_SHARE) - 1))
		switch (name[sizeof(CS_HOST_SHARE) - 1])
		{
		case 0:
			flags |= CS_ADDR_SHARE;
			if (sp = csinfo(state, name, NiL))
			{
				while (name = sfgetr(sp, '\n', 1))
					if (addr = realaddr(state, name))
						goto ok;
				sfclose(sp);
				sp = 0;
			}
			if (!(addr = realaddr(state, name = CS_HOST_GATEWAY)))
				addr = local;
			goto ok;
		case '.':
			flags |= CS_ADDR_SHARE;
			name += sizeof(CS_HOST_SHARE);
			break;
		}
	if (addr = realaddr(state, name))
		goto ok;
	if ((flags & CS_ADDR_SHARE) && !(state->flags & CS_ADDR_NUMERIC) && (s = strchr(name, '.')))
	{
		char*			sb;
		char*			se;
		char*			sx;
		char*			t;
		char*			te;

		dot = s - name;
		s = state->temp;
		sx = &state->temp[sizeof(state->temp) - 1];
		s += sfsprintf(s, sx - s, "%s/", CS_SVC_REMOTE);
		sb = s;
		se = 0;
		t = name;
		while (*t && s < sx)
		{
			if (s - sb >= CS_MNT_MAX)
			{
				if (se)
				{
					s = se - 1;
					t = te;
				}
				else
				{
					if (s >= sx) break;
					if (*t == '.') t++;
				}
				*s++ = '/';
				sb = s;
				se = 0;
			}
			if ((*s++ = *t++) == '.')
			{
				se = s;
				te = t;
			}
		}
		*s = 0;
		if (sp = csinfo(state, state->temp, NiL))
		{
			while (t = sfgetr(sp, '\n', 1))
			{
				if (s = strchr(t, '@')) s++;
				else s = t;
				if (addr = realaddr(state, s))
				{
					name = s;
					if (!userlen && s != t)
					{
						userlen = s - t - 1;
						user = t;
					}
					goto ok;
				}
			}
			sfclose(sp);
			sp = 0;
		}
		sfsprintf(state->temp, sizeof(state->temp), "%s.%s", CS_HOST_GATEWAY, name);
		if (addr = realaddr(state, state->temp))
		{
			name = state->temp;
			goto ok;
		}
		sfsprintf(state->temp, sizeof(state->temp), "%-.*s.%s", dot, name, name);
		if (addr = realaddr(state, state->temp))
		{
			name = state->temp;
			goto ok;
		}
	}
	messagef((state->id, NiL, -1, "addr: %s: not found", aname));
	return 0;
 ok:
	if (state->flags & CS_ADDR_NUMERIC)
		flags &= ~CS_ADDR_SHARE;
	if (addr == local)
	{
		flags |= CS_ADDR_LOCAL;
		name = csname(state, 0);
	}
	else if (!(state->flags & CS_ADDR_NUMERIC))
	{
		if (s = strchr(name, '.'))
		{
			sfsprintf(state->temp, sizeof(state->temp), "%-.*s", s - name, name);
			if (realaddr(state, state->temp) == addr)
				name = state->temp;
			else flags |= CS_ADDR_REMOTE;
		}
		if (!(flags & CS_ADDR_REMOTE) && !streq(name, CS_HOST_PROXY) && !csattr(state, name, "*"))
			flags |= CS_ADDR_REMOTE;
	}

	/*
	 * cache host name and user for possible CS_REMOTE_SHELL
	 */

	strncpy(state->host, name, sizeof(state->host) - 1);
	if (userlen)
	{
		if (userlen >= sizeof(state->user)) userlen = sizeof(state->user) - 1;
		strncpy(state->user, user, userlen);
	}
	state->user[userlen] = 0;
	if (sp) sfclose(sp);
	state->flags &= ~(CS_ADDR_LOCAL|CS_ADDR_NOW|CS_ADDR_REMOTE|CS_ADDR_SHARE|CS_DAEMON_SLAVE|CS_ADDR_TEST|CS_ADDR_TRUST);
	state->flags |= flags;
	messagef((state->id, NiL, -8, "addr(%s) = %s, flags = |%s%s%s", aname, csntoa(state, addr), (state->flags & CS_ADDR_LOCAL) ? "LOCAL|" : "", (state->flags & CS_ADDR_REMOTE) ? "REMOTE|" : "", (state->flags & CS_ADDR_SHARE) ? "SHARE|" : ""));
	return addr;
}