blob: 35787df17a03b3dc12b97ba2d304e1493895d30d [file] [log] [blame]
/* 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;
}