/* $Id: sfspath.C,v 1.8 2001/06/28 04:47:23 dm Exp $ */

/*
 *
 * Copyright (C) 1999 David Mazieres (dm@uun.org)
 *
 * 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 "sfsmisc.h"
#include "crypt.h"
#include "parseopt.h"

bool
sfs_ascii2hostid (sfs_hash *hid, const char *p)
{
  const u_char *const up = reinterpret_cast<const u_char *> (p);
  if (armor32len (up) != ascii_hostid_len || p[ascii_hostid_len])
    return false;
  if (hid) {
    str raw = dearmor32 (p, ascii_hostid_len);
    memcpy (hid->base (), raw, hid->size ());
  }
  return true;
}

bool
sfsgethost_label (const char *&p)
{
  const char *s = p;
  while ((*s >= 'a' && *s <= 'z')
	 || (*s >= '0' && *s <= '9') || *s == '-')
    s++;
  if (s - p == 0 || s - p > 63)
    return false;
  p = s;
  return true;
}
bool
sfsgethost_dotlabel (const char *&p)
{
  const char *s = p;
  if (*s++ != '.')
    return false;
  if (!sfsgethost_label (s))
    return false;
  p = s;
  return true;
}
str
sfsgethost (const char *&p)
{
  const char *s = p;
  if (!sfsgethost_label (s) || !sfsgethost_dotlabel (s))
    return NULL;
  while (sfsgethost_dotlabel (s))
    ;
  if (s - p > 255)
    return NULL;

  str host (p, s - p);
  p = s;
  return host;
}

bool
sfs_parsepath (str path, str *host, sfs_hash *hostid, u_int16_t *portp)
{
  const char *p = path;
  u_int16_t port = sfs_port;

  if (isdigit (*p)) {
    int64_t atno = strtoi64 (path, const_cast<char **> (&p), 10);
    if (p && *p == '@' && atno > 0 && atno < 0x10000) {
      port = atno;
      path = substr (path, p + 1 - path.cstr ());
    }
    p = path;
  }

  if (portp)
    *portp = port;
  if (!sfsgethost (p))
    return false;
  if (host)
    host->setbuf (path, p - path);
  return *p++ == ':' && sfs_ascii2hostid (hostid, p);
}


bool
sfs_mkhostid (sfs_hash *id, const sfs_hostinfo &info)
{
  const char *p = info.hostname;
  if (info.type != SFS_HOSTINFO || !sfsgethost (p) || *p) {
    bzero (id->base (), id->size ());
    return false;
  }

  xdrsuio x;
  if (!xdr_sfs_hostinfo (&x, const_cast<sfs_hostinfo *> (&info))
      || !xdr_sfs_hostinfo (&x, const_cast<sfs_hostinfo *> (&info))) {
    warn << "sfs_mkhostid (" << info.hostname << ", "
	 << hexdump (id->base (), id->size ()) << "): XDR failed!\n";
    bzero (id->base (), id->size ());
    return false;
  }
  sha1_hashv (id->base (), x.iov (), x.iovcnt ());
  return true;
}

str
sfs_hostinfo2path (const sfs_hostinfo &info)
{
  sfs_hash hostid;
  if (!sfs_mkhostid (&hostid, info))
    return NULL;
  return strbuf () << info.hostname << ":"
		   << armor32 (&hostid, sizeof (hostid));
}

bool
sfs_ckhostid (const sfs_hash *id, const sfs_hostinfo &info)
{
  sfs_hash rid;
  return sfs_mkhostid (&rid, info) && !memcmp (&rid, id, sizeof (rid));
}

bool
sfs_ckpath (str sname, const sfs_hostinfo &info)
{
  str location;
  sfs_hash hid;
  return sfs_parsepath (sname, &location, &hid)
    && location == info.hostname && sfs_ckhostid (&hid, info);
}

bool
sfs_ckci (const sfs_connectinfo &ci, const sfs_hostinfo &info)
{
  if (ci.civers <= 4)
    return sfs_ckhostid (&ci.ci4->hostid, info);
  return sfs_ckpath (ci.ci5->sname, info);
}

bool
sfs_checkrevoke (const sfs_pathrevoke &rev)
{
  if (rev.msg.type != SFS_PATHREVOKE
      || rev.msg.path.type != SFS_HOSTINFO
      || (rev.msg.redirect && rev.msg.redirect->hostinfo.type != SFS_HOSTINFO))
    return false;

  rabin_pub pk (rev.msg.path.pubkey);
  if (!pk.verify (xdr2str (rev.msg), rev.sig))
    return false;

  sfs_hash hostid;
  if (rev.msg.redirect &&
      (!sfs_mkhostid (&hostid, rev.msg.redirect->hostinfo)
       || implicit_cast<time_t> (rev.msg.redirect->expire) >= timenow))
    return false;

  return true;
}
