/* $Id: sfsconnect.C,v 1.5 2001/07/10 17:44:15 ericp Exp $ */ /* * * Copyright (C) 1999 David Mazieres (dm@uun.org) * Copyright (C) 2000 Michael Kaminsky (kaminsky@lcs.mit.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * */ #include "sfsconnect.h" void sfs_initci (sfs_connectinfo *ci, str path, sfs_service service, vec *exts) { ci->set_civers (5); ci->ci5->release = sfs_release; ci->ci5->service = service; ci->ci5->sname = path; if (exts) ci->ci5->extensions.set (exts->base (), exts->size (), freemode::NOFREE); else ci->ci5->extensions.setsize (0); } bool sfs_nextci (sfs_connectinfo *ci) { if (ci->civers == 5) { sfs_service service = ci->ci5->service; str sname = ci->ci5->sname; rpc_vec exts; exts.swap (ci->ci5->extensions); ci->set_civers (4); ci->ci4->service = service; if (!sfs_parsepath (sname, &ci->ci4->name, &ci->ci4->hostid)) ci->ci4->name = sname; exts.swap (ci->ci4->extensions); return true; } return false; } struct constate { static ptr ckey; const sfs_connect_cb cb; str sname; u_int16_t port; str location; sfs_connectarg carg; sfs_connectres cres; ref sc; ptr c; bool encrypt; bool check_hostid; constate (const sfs_connect_cb &c) : cb (c), port (sfs_port), sc (New refcounted), encrypt (true), check_hostid (true) {} void fail (str msg) { (*cb) (NULL, msg); delete this; } void succeed () { c = NULL; (*cb) (sc, NULL); delete this; } void start (); void getfd (int fd); void sendconnect (); void getconres (enum clnt_stat err); void cryptcb (const sfs_hash *sessidp); }; ptr constate::ckey; void constate::start () { /* XXX */ // if (encrypt) // rndstart (); if (sname == "-" || (location && location == "-")) { location = "localhost"; int fd = suidgetfd ("authserv"); errno = ECONNREFUSED; getfd (fd); } else if (!location && !sfs_parsepath (sname, &location, NULL, &port)) fail (sname << ": cannot parse path\n"); else if (!strchr (location, '.') && check_hostid) fail (location << ": must contain fully qualified domain name\n"); else tcpconnect (location, port, wrap (this, &constate::getfd)); } void constate::getfd (int fd) { if (fd < 0) { fail (location << ": " << strerror (errno)); return; } sc->x = axprt_crypt::alloc (fd); c = aclnt::alloc (sc->x, sfs_program_1); sendconnect (); } void constate::sendconnect () { c->call (SFSPROC_CONNECT, &carg, &cres, wrap (this, &constate::getconres)); } void constate::getconres (enum clnt_stat err) { if (err == RPC_CANTDECODEARGS && sfs_nextci (&carg)) { sendconnect (); return; } if (err) { fail (location << ": " << err); return; } if (cres.status) { fail (location << ": " << cres.status); return; } sc->servinfo = cres.reply->servinfo; if (!sfs_mkhostid (&sc->hostid, sc->servinfo.host)) { fail (location << ": Server returned garbage hostinfo"); return; } if (sfs_ckpath (sname, sc->servinfo.host)) sc->hostid_valid = true; else if (check_hostid) { fail (location << ": Server key does not match hostid"); return; } if (port == sfs_port) sc->path = sfs_hostinfo2path (sc->servinfo.host); else sc->path = strbuf ("%d@", port) << sfs_hostinfo2path (sc->servinfo.host); if (encrypt) { if (!ckey) { // rndsync (); /* XXX */ if (!ckey) ckey = New refcounted (rabin_keygen (sfs_minpubkeysize)); } sfs_client_crypt (c, ckey, carg, *cres.reply, wrap (this, &constate::cryptcb)); } else succeed (); } void constate::cryptcb (const sfs_hash *sidp) { if (!sidp) { fail (location << ": Session key negotiation failed"); return; } sc->sessid = *sidp; sfs_get_authid (&sc->authid, carg.civers == 4 ? carg.ci4->service : carg.ci5->service, sc->servinfo.host.hostname, &sc->hostid, &sc->sessid); succeed (); } void sfs_connect (const sfs_connectarg &carg, sfs_connect_cb cb, bool encrypt, bool check_hostid) { constate *sc = New constate (cb); assert (carg.civers == 5); sc->carg = carg; sc->sname = carg.ci5->sname; sc->encrypt = encrypt; sc->check_hostid = check_hostid; sc->start (); } void sfs_connect_path (str path, sfs_service service, sfs_connect_cb cb, bool encrypt, bool check_hostid) { constate *cs = New constate (cb); cs->sname = path; sfs_initci (&cs->carg, path, service); cs->encrypt = encrypt; cs->check_hostid = check_hostid; cs->start (); } void sfs_connect_local_cb (constate *cs, ptr sc, str err) { if (sc) { cs->sname = sc->path; cs->check_hostid = true; cs->start (); } else cs->fail (err); } void sfs_connect_host (str host, sfs_service service, sfs_connect_cb cb, bool encrypt) { constate *cs = New constate (cb); cs->sname = cs->location = host; sfs_initci (&cs->carg, host, service); cs->encrypt = encrypt; cs->check_hostid = false; if (host == "-") { sfs_connectarg carg; sfs_initci (&carg, host, SFS_AUTHSERV); sfs_connect (carg, wrap (sfs_connect_local_cb, cs), false, false); return; } cs->start (); }