| /* Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved. | 
 |  * Portions Copyright © 1997-1999 Vita Nuova Limited | 
 |  * Portions Copyright © 2000-2007 Vita Nuova Holdings Limited | 
 |  *                                (www.vitanuova.com) | 
 |  * Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others | 
 |  * | 
 |  * Modified for the Akaros operating system: | 
 |  * Copyright (c) 2013-2014 The Regents of the University of California | 
 |  * Copyright (c) 2013-2015 Google Inc. | 
 |  * | 
 |  * Permission is hereby granted, free of charge, to any person obtaining a copy | 
 |  * of this software and associated documentation files (the "Software"), to deal | 
 |  * in the Software without restriction, including without limitation the rights | 
 |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
 |  * copies of the Software, and to permit persons to whom the Software is | 
 |  * furnished to do so, subject to the following conditions: | 
 |  * | 
 |  * The above copyright notice and this permission notice shall be included in | 
 |  * all copies or substantial portions of the Software. | 
 |  * | 
 |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
 |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
 |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE | 
 |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
 |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
 |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
 |  * SOFTWARE. */ | 
 |  | 
 | #include <slab.h> | 
 | #include <kmalloc.h> | 
 | #include <kref.h> | 
 | #include <string.h> | 
 | #include <stdio.h> | 
 | #include <assert.h> | 
 | #include <error.h> | 
 | #include <cpio.h> | 
 | #include <pmap.h> | 
 | #include <smp.h> | 
 | #include <net/ip.h> | 
 |  | 
 | typedef struct DS DS; | 
 |  | 
 | static int call(char *cp, char *cp1, DS * DS); | 
 | static int csdial(DS * DS); | 
 | static void _dial_string_parse(char *cp, DS * DS); | 
 | static int nettrans(char *cp, char *cp1, int na, char *cp2, int i); | 
 |  | 
 | enum { | 
 | 	Maxstring = 128, | 
 | }; | 
 |  | 
 | struct DS { | 
 | 	char buf[Maxstring];		/* dist string */ | 
 | 	char *netdir; | 
 | 	char *proto; | 
 | 	char *rem; | 
 | 	char *local;			/* other args */ | 
 | 	char *dir; | 
 | 	int *cfdp; | 
 | }; | 
 |  | 
 | /* only used here for now. */ | 
 | static void kerrstr(void *err, int len) | 
 | { | 
 | 	strlcpy(err, current_errstr(), len); | 
 | } | 
 |  | 
 | /* | 
 |  *  the dialstring is of the form '[/net/]proto!dest' | 
 |  */ | 
 | int kdial(char *dest, char *local, char *dir, int *cfdp) | 
 | { | 
 | 	DS ds; | 
 | 	int rv; | 
 | 	char *err, *alterr; | 
 |  | 
 | 	err = kmalloc(ERRMAX, MEM_WAIT); | 
 | 	alterr = kmalloc(ERRMAX, MEM_WAIT); | 
 |  | 
 | 	ds.local = local; | 
 | 	ds.dir = dir; | 
 | 	ds.cfdp = cfdp; | 
 |  | 
 | 	_dial_string_parse(dest, &ds); | 
 | 	if (ds.netdir) { | 
 | 		rv = csdial(&ds); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	ds.netdir = "/net"; | 
 | 	rv = csdial(&ds); | 
 | 	if (rv >= 0) | 
 | 		goto out; | 
 |  | 
 | 	err[0] = 0; | 
 | 	strlcpy(err, current_errstr(), ERRMAX); | 
 | 	if (strstr(err, "refused") != 0) { | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	ds.netdir = "/net.alt"; | 
 | 	rv = csdial(&ds); | 
 | 	if (rv >= 0) | 
 | 		goto out; | 
 |  | 
 | 	alterr[0] = 0; | 
 | 	kerrstr(alterr, ERRMAX); | 
 |  | 
 | 	if (strstr(alterr, "translate") || strstr(alterr, "does not exist")) | 
 | 		kerrstr(err, ERRMAX); | 
 | 	else | 
 | 		kerrstr(alterr, ERRMAX); | 
 | out: | 
 | 	kfree(err); | 
 | 	kfree(alterr); | 
 | 	return rv; | 
 | } | 
 |  | 
 | static int csdial(DS * ds) | 
 | { | 
 | 	int n, fd, rv = -1; | 
 | 	char *p, *buf, *clone, *err, *besterr; | 
 |  | 
 | 	buf = kmalloc(Maxstring, MEM_WAIT); | 
 | 	clone = kmalloc(Maxpath, MEM_WAIT); | 
 | 	err = kmalloc(ERRMAX, MEM_WAIT); | 
 | 	besterr = kmalloc(ERRMAX, MEM_WAIT); | 
 | 	/* | 
 | 	 *  open connection server | 
 | 	 */ | 
 | 	snprintf(buf, Maxstring, "%s/cs", ds->netdir); | 
 | 	fd = sysopen(buf, O_RDWR); | 
 | 	if (fd < 0) { | 
 | 		/* no connection server, don't translate */ | 
 | 		snprintf(clone, Maxpath, "%s/%s/clone", ds->netdir, ds->proto); | 
 | 		rv = call(clone, ds->rem, ds); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 *  ask connection server to translate | 
 | 	 */ | 
 | 	snprintf(buf, Maxstring, "%s!%s", ds->proto, ds->rem); | 
 | 	if (syswrite(fd, buf, strlen(buf)) < 0) { | 
 | 		kerrstr(err, ERRMAX); | 
 | 		sysclose(fd); | 
 | 		set_errstr("%s (%s)", err, buf); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 *  loop through each address from the connection server till | 
 | 	 *  we get one that works. | 
 | 	 */ | 
 | 	*besterr = 0; | 
 | 	strlcpy(err, "csdial() connection reset", ERRMAX); | 
 | 	sysseek(fd, 0, 0); | 
 | 	while ((n = sysread(fd, buf, Maxstring - 1)) > 0) { | 
 | 		buf[n] = 0; | 
 | 		p = strchr(buf, ' '); | 
 | 		if (p == 0) | 
 | 			continue; | 
 | 		*p++ = 0; | 
 | 		rv = call(buf, p, ds); | 
 | 		if (rv >= 0) | 
 | 			break; | 
 | 		err[0] = 0; | 
 | 		kerrstr(err, ERRMAX); | 
 | 		if (strstr(err, "does not exist") == 0) | 
 | 			memmove(besterr, err, ERRMAX); | 
 | 	} | 
 | 	sysclose(fd); | 
 |  | 
 | 	if (rv < 0 && *besterr) | 
 | 		kerrstr(besterr, ERRMAX); | 
 | 	else | 
 | 		kerrstr(err, ERRMAX); | 
 | out: | 
 | 	kfree(buf); | 
 | 	kfree(clone); | 
 | 	kfree(err); | 
 | 	kfree(besterr); | 
 | 	return rv; | 
 | } | 
 |  | 
 | static int call(char *clone, char *dest, DS * ds) | 
 | { | 
 | 	int fd, cfd, n, retval; | 
 | 	char *name, *data, *err, *p; | 
 |  | 
 | 	name = kmalloc(Maxpath, MEM_WAIT); | 
 | 	data = kmalloc(Maxpath, MEM_WAIT); | 
 | 	err = kmalloc(ERRMAX, MEM_WAIT); | 
 |  | 
 | 	cfd = sysopen(clone, O_RDWR); | 
 | 	if (cfd < 0) { | 
 | 		kerrstr(err, ERRMAX); | 
 | 		set_errstr("%s (%s)", err, clone); | 
 | 		retval = -1; | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	/* get directory name */ | 
 | 	n = sysread(cfd, name, Maxpath - 1); | 
 | 	if (n < 0) { | 
 | 		kerrstr(err, ERRMAX); | 
 | 		sysclose(cfd); | 
 | 		set_errstr("read %s: %s", clone, err); | 
 | 		retval = -1; | 
 | 		goto out; | 
 | 	} | 
 | 	name[n] = 0; | 
 | 	for (p = name; *p == ' '; p++) ; | 
 | 	snprintf(name, Maxpath, "%ld", strtoul(p, 0, 0)); | 
 | 	p = strrchr(clone, '/'); | 
 | 	*p = 0; | 
 | 	if (ds->dir) | 
 | 		snprintf(ds->dir, NETPATHLEN, "%s/%s", clone, name); | 
 | 	snprintf(data, Maxpath, "%s/%s/data", clone, name); | 
 |  | 
 | 	/* connect */ | 
 | 	if (ds->local) | 
 | 		snprintf(name, Maxpath, "connect %s %s", dest, ds->local); | 
 | 	else | 
 | 		snprintf(name, Maxpath, "connect %s", dest); | 
 | 	if (syswrite(cfd, name, strlen(name)) < 0) { | 
 | 		err[0] = 0; | 
 | 		kerrstr(err, ERRMAX); | 
 | 		sysclose(cfd); | 
 | 		set_errstr("%s (%s)", err, name); | 
 | 		retval = -1; | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	/* open data connection */ | 
 | 	fd = sysopen(data, O_RDWR); | 
 | 	if (fd < 0) { | 
 | 		err[0] = 0; | 
 | 		kerrstr(err, ERRMAX); | 
 | 		set_errstr("%s (%s)", err, data); | 
 | 		sysclose(cfd); | 
 | 		retval = -1; | 
 | 		goto out; | 
 | 	} | 
 | 	if (ds->cfdp) | 
 | 		*ds->cfdp = cfd; | 
 | 	else | 
 | 		sysclose(cfd); | 
 | 	retval = fd; | 
 | out: | 
 | 	kfree(name); | 
 | 	kfree(data); | 
 | 	kfree(err); | 
 |  | 
 | 	return retval; | 
 | } | 
 |  | 
 | /* | 
 |  *  parse a dial string | 
 |  */ | 
 | static void _dial_string_parse(char *str, DS * ds) | 
 | { | 
 | 	char *p, *p2; | 
 |  | 
 | 	strlcpy(ds->buf, str, Maxstring); | 
 |  | 
 | 	p = strchr(ds->buf, '!'); | 
 | 	if (p == 0) { | 
 | 		ds->netdir = 0; | 
 | 		ds->proto = "net"; | 
 | 		ds->rem = ds->buf; | 
 | 	} else { | 
 | 		if (*ds->buf != '/' && *ds->buf != '#') { | 
 | 			ds->netdir = 0; | 
 | 			ds->proto = ds->buf; | 
 | 		} else { | 
 | 			for (p2 = p; *p2 != '/'; p2--) ; | 
 | 			*p2++ = 0; | 
 | 			ds->netdir = ds->buf; | 
 | 			ds->proto = p2; | 
 | 		} | 
 | 		*p = 0; | 
 | 		ds->rem = p + 1; | 
 | 	} | 
 | } | 
 |  | 
 | /* | 
 |  *  announce a network service. | 
 |  */ | 
 | int kannounce(char *addr, char *dir, size_t dirlen) | 
 | { | 
 | 	int ctl, n, m; | 
 | 	char buf[NETPATHLEN]; | 
 | 	char buf2[Maxpath]; | 
 | 	char netdir[NETPATHLEN]; | 
 | 	char naddr[Maxpath]; | 
 | 	char *cp; | 
 |  | 
 | 	/* | 
 | 	 *  translate the address | 
 | 	 */ | 
 | 	if (nettrans(addr, naddr, sizeof(naddr), netdir, sizeof(netdir)) < 0) | 
 | 		return -1; | 
 |  | 
 | 	/* | 
 | 	 * get a control channel | 
 | 	 */ | 
 | 	ctl = sysopen(netdir, O_RDWR); | 
 | 	if (ctl < 0) | 
 | 		return -1; | 
 | 	cp = strrchr(netdir, '/'); | 
 | 	*cp = 0; | 
 |  | 
 | 	/* | 
 | 	 *  find out which line we have | 
 | 	 */ | 
 | 	n = snprintf(buf, sizeof(buf), "%.*s/", sizeof buf, netdir); | 
 | 	m = sysread(ctl, &buf[n], sizeof(buf) - n - 1); | 
 | 	if (m <= 0) { | 
 | 		sysclose(ctl); | 
 | 		return -1; | 
 | 	} | 
 | 	buf[n + m] = 0; | 
 |  | 
 | 	/* | 
 | 	 *  make the call | 
 | 	 */ | 
 | 	n = snprintf(buf2, sizeof buf2, "announce %s", naddr); | 
 | 	if (syswrite(ctl, buf2, n) != n) { | 
 | 		sysclose(ctl); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 *  return directory etc. | 
 | 	 */ | 
 | 	if (dir) | 
 | 		strlcpy(dir, buf, dirlen); | 
 | 	return ctl; | 
 | } | 
 |  | 
 | /* | 
 |  *  listen for an incoming call | 
 |  */ | 
 | int klisten(char *dir, char *newdir, size_t newdirlen) | 
 | { | 
 | 	int ctl, n, m; | 
 | 	char buf[NETPATHLEN + 1]; | 
 | 	char *cp; | 
 |  | 
 | 	/* | 
 | 	 *  open listen, wait for a call | 
 | 	 */ | 
 | 	snprintf(buf, sizeof buf, "%s/listen", dir); | 
 | 	ctl = sysopen(buf, O_RDWR); | 
 | 	if (ctl < 0) | 
 | 		return -1; | 
 |  | 
 | 	/* | 
 | 	 *  find out which line we have | 
 | 	 */ | 
 | 	strlcpy(buf, dir, sizeof(buf)); | 
 | 	cp = strrchr(buf, '/'); | 
 | 	*++cp = 0; | 
 | 	n = cp - buf; | 
 | 	m = sysread(ctl, cp, sizeof(buf) - n - 1); | 
 | 	if (m <= 0) { | 
 | 		sysclose(ctl); | 
 | 		return -1; | 
 | 	} | 
 | 	buf[n + m] = 0; | 
 |  | 
 | 	/* | 
 | 	 *  return directory etc. | 
 | 	 */ | 
 | 	if (newdir) | 
 | 		strlcpy(newdir, buf, newdirlen); | 
 | 	return ctl; | 
 |  | 
 | } | 
 |  | 
 | /* | 
 |  *  perform the identity translation (in case we can't reach cs) | 
 |  */ | 
 | static int identtrans(char *netdir, char *addr, char *naddr, int na, char *file, | 
 | 		      int nf) | 
 | { | 
 | 	char proto[Maxpath]; | 
 | 	char *p; | 
 |  | 
 | 	/* parse the protocol */ | 
 | 	strlcpy(proto, addr, sizeof(proto)); | 
 | 	p = strchr(proto, '!'); | 
 | 	if (p) | 
 | 		*p++ = 0; | 
 |  | 
 | 	snprintf(file, nf, "%s/%s/clone", netdir, proto); | 
 | 	strlcpy(naddr, p, na); | 
 |  | 
 | 	return 1; | 
 | } | 
 |  | 
 | /* | 
 |  *  call up the connection server and get a translation | 
 |  */ | 
 | static int nettrans(char *addr, char *naddr, int na, char *file, int nf) | 
 | { | 
 | 	int i, fd; | 
 | 	char buf[Maxpath]; | 
 | 	char netdir[NETPATHLEN]; | 
 | 	char *p, *p2; | 
 | 	long n; | 
 |  | 
 | 	/* | 
 | 	 *  parse, get network directory | 
 | 	 */ | 
 | 	p = strchr(addr, '!'); | 
 | 	if (p == 0) { | 
 | 		set_errstr("bad dial string: %s", addr); | 
 | 		return -1; | 
 | 	} | 
 | 	if (*addr != '/') { | 
 | 		strlcpy(netdir, "/net", sizeof(netdir)); | 
 | 	} else { | 
 | 		for (p2 = p; *p2 != '/'; p2--) ; | 
 | 		i = p2 - addr; | 
 | 		if (i == 0 || i >= sizeof(netdir)) { | 
 | 			set_errstr("bad dial string: %s", addr); | 
 | 			return -1; | 
 | 		} | 
 | 		strlcpy(netdir, addr, i + 1); | 
 | 		addr = p2 + 1; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 *  ask the connection server | 
 | 	 */ | 
 | 	snprintf(buf, sizeof(buf), "%s/cs", netdir); | 
 | 	fd = sysopen(buf, O_RDWR); | 
 | 	if (fd < 0) | 
 | 		return identtrans(netdir, addr, naddr, na, file, nf); | 
 | 	if (syswrite(fd, addr, strlen(addr)) < 0) { | 
 | 		sysclose(fd); | 
 | 		return -1; | 
 | 	} | 
 | 	sysseek(fd, 0, 0); | 
 | 	n = sysread(fd, buf, sizeof(buf) - 1); | 
 | 	sysclose(fd); | 
 | 	if (n <= 0) | 
 | 		return -1; | 
 | 	buf[n] = 0; | 
 |  | 
 | 	/* | 
 | 	 *  parse the reply | 
 | 	 */ | 
 | 	p = strchr(buf, ' '); | 
 | 	if (p == 0) | 
 | 		return -1; | 
 | 	*p++ = 0; | 
 | 	strlcpy(naddr, p, na); | 
 | 	strlcpy(file, buf, nf); | 
 | 	return 0; | 
 | } |