chiark / gitweb /
'cookie' response now includes the username.
authorRichard Kettlewell <rjk@greenend.org.uk>
Sat, 22 Dec 2007 15:34:58 +0000 (15:34 +0000)
committerRichard Kettlewell <rjk@greenend.org.uk>
Sat, 22 Dec 2007 15:34:58 +0000 (15:34 +0000)
Rewrote the mad address lookup code shared by client/eclient.

disorder_connect_cookie() connects trying to use a cookie and backing
off to guest otherwise.

doc/disorder_protocol.5.in
lib/client-common.c
lib/client-common.h
lib/client.c
lib/client.h
lib/eclient.c
server/server.c

index 15c83967c52e5a64a79aa76747bf7148bc4b4cba..6bb28572820c440c11d81832eb3e51e41149a12c 100644 (file)
@@ -65,7 +65,8 @@ Confirm user registration.  \fICONFIRMATION\fR is as returned from
 \fBregister\fR below.  This command can be used without logging in.
 .TP
 .B cookie \fICOOKIE
-Log a user back in using a cookie created with \fBmake-cookie\fR.
+Log a user back in using a cookie created with \fBmake-cookie\fR.  The response
+contains the username.
 .TP
 .B deluser \fIUSERNAME
 Delete the named user.  Requires the \fBadmin\fR right, and only works on
index e1fcce9eb59dba1d820ddcb2ef1c1ba3e358269e..d2691298de514819372416353aa76063001860d8 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * This file is part of DisOrder
- * Copyright (C) 2004, 2005, 2006 Richard Kettlewell
+ * Copyright (C) 2004, 2005, 2006, 2007 Richard Kettlewell
  *
  * 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
 #include "configuration.h"
 #include "client-common.h"
 #include "addr.h"
+#include "mem.h"
 
-/** @brief Invoke a function with the connect address
- * @param c Passed to callback
- * @param function Function to call
- *
- * Calls @p function with the result of looking up the connect address.
+/** @brief Figure out what address to connect to
+ * @param sap Where to store pointer to sockaddr
+ * @param namep Where to store socket name
+ * @return Socket length, or (socklen_t)-1
  */
-int with_sockaddr(void *c,
-                 int (*function)(void *c,
-                                 const struct sockaddr *sa,
-                                 socklen_t len,
-                                 const char *ident)) {
-  const char *path;
+socklen_t find_server(struct sockaddr **sap, char **namep) {
+  struct sockaddr *sa;
   struct sockaddr_un su;
-  struct addrinfo *res;
+  struct addrinfo *res = 0;
   char *name;
-  int n;
+  socklen_t len;
    
   static const struct addrinfo pref = {
     0,
@@ -65,20 +61,27 @@ int with_sockaddr(void *c,
   if(config->connect.n) {
     res = get_address(&config->connect, &pref, &name);
     if(!res) return -1;
-    n = function(c, res->ai_addr, res->ai_addrlen, name);
-    freeaddrinfo(res);
-    return n;
+    sa = res->ai_addr;
+    len = res->ai_addrlen;
   } else {
-    path = config_get_file("socket");
-    if(strlen(path) >= sizeof su.sun_path) {
+    name = config_get_file("socket");
+    if(strlen(name) >= sizeof su.sun_path) {
       error(errno, "socket path is too long");
       return -1;
     }
     memset(&su, 0, sizeof su);
     su.sun_family = AF_UNIX;
-    strcpy(su.sun_path, path);
-    return function(c, (struct sockaddr *)&su, sizeof su, path);
+    strcpy(su.sun_path, name);
+    sa = (struct sockaddr *)&su;
+    len = sizeof su;
   }
+  *sap = xmalloc_noptr(len);
+  memcpy(*sap, sa, len);
+  if(namep)
+    *namep = name;
+  if(res)
+    freeaddrinfo(res);
+  return len;
 }
 
 /*
index 0d883695fa3caa5408dfbb4a4a59e119de382554..bf7e0b5eb40f190c55680000fdf5f21554b94a9b 100644 (file)
 #ifndef CLIENT_COMMON_H
 #define CLIENT_COMMON_H
 
-int with_sockaddr(void *c,
-                  int (*function)(void *c,
-                                  const struct sockaddr *sa,
-                                  socklen_t len,
-                                  const char *ident));
+socklen_t find_server(struct sockaddr **sap, char **namep);
 
 #endif /* CLIENT_COMMON_H */
 
index 0c12c90167aed0efa160812111c1d42bc7d7e793..fcb0dba6740ac0774b3a0ebab8c77011bb72217d 100644 (file)
@@ -66,19 +66,13 @@ struct disorder_client {
   int verbose;
 };
 
-static int disorder_connect_sock(disorder_client *c,
-                                const struct sockaddr *sa,
-                                socklen_t len,
-                                const char *username,
-                                const char *password,
-                                const char *ident);
-
 /** @brief Create a new client
  * @param verbose If nonzero, write extra junk to stderr
  * @return Pointer to new client
  *
- * You must call disorder_connect() to connect it.  Use
- * disorder_close() to dispose of the client when finished with it.
+ * You must call disorder_connect() or disorder_connect_cookie() to
+ * connect it.  Use disorder_close() to dispose of the client when
+ * finished with it.
  */
 disorder_client *disorder_new(int verbose) {
   disorder_client *c = xmalloc(sizeof (struct disorder_client));
@@ -208,68 +202,58 @@ static int disorder_simple(disorder_client *c,
   return ret;
 }
 
-static int connect_sock(void *vc,
-                       const struct sockaddr *sa,
-                       socklen_t len,
-                       const char *ident) {
-  const char *username, *password;
-  disorder_client *c = vc;
-  
-  if(!(username = config->username)) {
-    error(0, "no username configured");
-    return -1;
-  }
-  password = config->password;
-  if(!password) {
-    /* Maybe we can read the database */
-    /* TODO failure to open the database should not be fatal */
-    trackdb_init(TRACKDB_NO_RECOVER|TRACKDB_NO_UPGRADE);
-    trackdb_open(TRACKDB_READ_ONLY);
-    password = trackdb_get_password(username);
-    trackdb_close();
-  }
-  if(!password) {
-    /* Oh well */
-    error(0, "no password configured");
-    return -1;
+/** @brief Dequote a result string
+ * @param rc 0 on success, non-0 on error
+ * @param rp Where result string is stored (UTF-8)
+ * @return @p rc
+ *
+ * This is used as a wrapper around disorder_simple() to dequote
+ * results in place.
+ */
+static int dequote(int rc, char **rp) {
+  char **rr;
+  if(!rc) {
+    if((rr = split(*rp, 0, SPLIT_QUOTES, 0, 0)) && *rr) {
+      *rp = *rr;
+      return 0;
+    }
+    error(0, "invalid reply: %s", *rp);
   }
-  return disorder_connect_sock(c, sa, len, username, password, ident);
+  return -1;
 }
 
-/** @brief Connect a client
+/** @brief Generic connection routine
  * @param c Client
+ * @param username Username to log in with or NULL
+ * @param password Password to log in with or NULL
+ * @param cookie Cookie to log in with or NULL
  * @return 0 on success, non-0 on error
  *
- * The connection will use the username and password found in @ref
- * config.
+ * @p cookie is tried first if not NULL.  If it is NULL then @p
+ * username must not be.  If @p username is not NULL then nor may @p
+ * password be.
  */
-int disorder_connect(disorder_client *c) {
-  return with_sockaddr(c, connect_sock);
-}
-
-static int disorder_connect_sock(disorder_client *c,
-                                const struct sockaddr *sa,
-                                socklen_t len,
-                                const char *username,
-                                const char *password,
-                                const char *ident) {
+static int disorder_connect_generic(disorder_client *c,
+                                   const char *username,
+                                   const char *password,
+                                   const char *cookie) {
   int fd = -1, fd2 = -1, nrvec;
   unsigned char *nonce;
   size_t nl;
   const char *res;
   char *r, **rvec;
   const char *protocol, *algorithm, *challenge;
+  struct sockaddr *sa;
+  socklen_t salen;
 
-  if(!password) {
-    error(0, "no password found");
+  if((salen = find_server(&sa, &c->ident)) == (socklen_t)-1)
     return -1;
-  }
   c->fpin = c->fpout = 0;
   if((fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) {
     error(errno, "error calling socket");
     return -1;
   }
-  if(connect(fd, sa, len) < 0) {
+  if(connect(fd, sa, salen) < 0) {
     error(errno, "error calling connect");
     goto error;
   }
@@ -287,7 +271,6 @@ static int disorder_connect_sock(disorder_client *c,
     goto error;
   }
   fd2 = -1;
-  c->ident = xstrdup(ident);
   if(disorder_simple(c, &r, 0, (const char *)0))
     return -1;
   if(!(rvec = split(r, &nrvec, SPLIT_QUOTES, 0, 0)))
@@ -305,6 +288,15 @@ static int disorder_connect_sock(disorder_client *c,
   challenge = *rvec++;
   if(!(nonce = unhex(challenge, &nl)))
     return -1;
+  if(cookie) {
+    if(!dequote(disorder_simple(c, &c->user, "cookie", cookie, (char *)0),
+               &c->user))
+      return 0;                                /* success */
+    if(!username) {
+      error(0, "cookie did not work and no username available");
+      return -1;
+    }
+  }
   if(!(res = authhash(nonce, nl, password, algorithm))) goto error;
   if(disorder_simple(c, 0, "user", username, res, (char *)0))
     return -1;
@@ -318,6 +310,58 @@ error:
   return -1;
 }
 
+/** @brief Connect a client
+ * @param c Client
+ * @return 0 on success, non-0 on error
+ *
+ * The connection will use the username and password found in @ref
+ * config, or directly from the database if no password is found and
+ * the database is readable (usually only for root).
+ */
+int disorder_connect(disorder_client *c) {
+  const char *username, *password;
+
+  if(!(username = config->username)) {
+    error(0, "no username configured");
+    return -1;
+  }
+  password = config->password;
+  if(!password) {
+    /* Maybe we can read the database */
+    /* TODO failure to open the database should not be fatal */
+    trackdb_init(TRACKDB_NO_RECOVER|TRACKDB_NO_UPGRADE);
+    trackdb_open(TRACKDB_READ_ONLY);
+    password = trackdb_get_password(username);
+    trackdb_close();
+  }
+  if(!password) {
+    /* Oh well */
+    error(0, "no password configured");
+    return -1;
+  }
+  return disorder_connect_generic(c,
+                                 username,
+                                 password,
+                                 0);
+}
+
+/** @brief Connect a client
+ * @param c Client
+ * @param cookie Cookie to log in with, or NULL
+ * @return 0 on success, non-0 on error
+ *
+ * If @p cookie is NULL or does not work then we attempt to log in as
+ * guest instead (so when the cookie expires only an extra round trip
+ * is needed rathre than a complete new login).
+ */
+int disorder_connect_cookie(disorder_client *c,
+                           const char *cookie) {
+  return disorder_connect_generic(c,
+                                 "guest",
+                                 "",
+                                 cookie);
+}
+
 /** @brief Close a client
  * @param c Client
  * @return 0 on succcess, non-0 on errior
@@ -342,6 +386,8 @@ int disorder_close(disorder_client *c) {
     }
     c->fpout = 0;
   }
+  c->ident = 0;
+  c->user = 0;
   return 0;
 }
 
@@ -431,26 +477,6 @@ int disorder_rescan(disorder_client *c) {
   return disorder_simple(c, 0, "rescan", (char *)0);
 }
 
-/** @brief Dequote a result string
- * @param rc 0 on success, non-0 on error
- * @param rp Where result string is stored (UTF-8)
- * @return @p rc
- *
- * This is used as a wrapper around disorder_simple() to dequote
- * results in place.
- */
-static int dequote(int rc, char **rp) {
-  char **rr;
-  if(!rc) {
-    if((rr = split(*rp, 0, SPLIT_QUOTES, 0, 0)) && *rr) {
-      *rp = *rr;
-      return 0;
-    }
-    error(0, "invalid reply: %s", *rp);
-  }
-  return -1;
-}
-
 /** @brief Get server version number
  * @param c Client
  * @param rp Where to store version string (UTF-8)
@@ -593,7 +619,7 @@ static int disorder_simple_list(disorder_client *c,
 
 /** @brief List directories below @p dir
  * @param c Client
- * @param dir Directory to list (UTF-8)
+ * @param dir Directory to list, or NULL for root (UTF-8)
  * @param re Regexp that results must match, or NULL (UTF-8)
  * @param vecp Where to store list (UTF-8)
  * @param nvecp Where to store number of items, or NULL
@@ -606,7 +632,7 @@ int disorder_directories(disorder_client *c, const char *dir, const char *re,
 
 /** @brief List files below @p dir
  * @param c Client
- * @param dir Directory to list (UTF-8)
+ * @param dir Directory to list, or NULL for root (UTF-8)
  * @param re Regexp that results must match, or NULL (UTF-8)
  * @param vecp Where to store list (UTF-8)
  * @param nvecp Where to store number of items, or NULL
@@ -619,7 +645,7 @@ int disorder_files(disorder_client *c, const char *dir, const char *re,
 
 /** @brief List files and directories below @p dir
  * @param c Client
- * @param dir Directory to list (UTF-8)
+ * @param dir Directory to list, or NULL for root (UTF-8)
  * @param re Regexp that results must match, or NULL (UTF-8)
  * @param vecp Where to store list (UTF-8)
  * @param nvecp Where to store number of items, or NULL
index 32010818f4e2e3f2c82283a44fe02445de125e24..4ce194fba9596b3a7cf5fd073b4cfbf8211348e4 100644 (file)
@@ -36,6 +36,7 @@ struct sink;
 
 disorder_client *disorder_new(int verbose);
 int disorder_connect(disorder_client *c);
+int disorder_connect_cookie(disorder_client *c, const char *cookie);
 int disorder_close(disorder_client *c);
 int disorder_become(disorder_client *c, const char *user);
 int disorder_version(disorder_client *c, char **versionp);
index 012db987d9a1599b97de73100c88925667b65fda..0037fb5d362d352449bbb34cb5ff3105eb2fda2c 100644 (file)
@@ -114,7 +114,7 @@ struct operation {
 
 /** @brief Client structure */
 struct disorder_eclient {
-  const char *ident;
+  char *ident;
   int fd;                               /**< @brief connection to server */
   enum client_state state;              /**< @brief current state */
   int authenticated;                    /**< @brief true when authenicated */
@@ -150,15 +150,8 @@ struct disorder_eclient {
 
 /* Forward declarations ******************************************************/
 
-static int start_connect(void *cc,
-                        const struct sockaddr *sa,
-                        socklen_t len,
-                        const char *ident);
+static int start_connect(disorder_eclient *c);
 static void process_line(disorder_eclient *c, char *line);
-static int start_connect(void *cc,
-                        const struct sockaddr *sa,
-                        socklen_t len,
-                        const char *ident);
 static void maybe_connected(disorder_eclient *c);
 static void authbanner_opcallback(disorder_eclient *c,
                                   struct operation *op);
@@ -341,7 +334,7 @@ void disorder_eclient_polled(disorder_eclient *c, unsigned mode) {
       comms_error(c, "no password is configured");
       return;
     }
-    with_sockaddr(c, start_connect);
+    start_connect(c);
     /* might now be state_disconnected (on error), state_connecting (slow
      * connect) or state_connected (fast connect).  If state_disconnected then
      * we just rely on a periodic callback from the event loop sometime. */
@@ -467,14 +460,13 @@ void disorder_eclient_polled(disorder_eclient *c, unsigned mode) {
 }
 
 /** @brief Called to start connecting */
-static int start_connect(void *cc,
-                        const struct sockaddr *sa,
-                        socklen_t len,
-                        const char *ident) {
-  disorder_eclient *c = cc;
+static int start_connect(disorder_eclient *c) {
+  struct sockaddr *sa;
+  socklen_t len;
 
   D(("start_connect"));
-  c->ident = xstrdup(ident);
+  if((len = find_server(&sa, &c->ident)) == (socklen_t)-1)
+    return comms_error(c, "cannot look up server"); /* TODO better error */
   if(c->fd != -1) {
     xclose(c->fd);
     c->fd = -1;
@@ -494,7 +486,7 @@ static int start_connect(void *cc,
       return 0;
     default:
       /* Signal the error to the caller. */
-      return comms_error(c, "connecting to %s: %s", ident, strerror(errno));
+      return comms_error(c, "connecting to %s: %s", c->ident, strerror(errno));
     }
   } else
     c->state = state_connected;
index 54dfe556e9f8d38b01ab69cd4e09856bba3604d3..4fc1690c043b9650f97a414c8a8ac19a8ec4f826 100644 (file)
@@ -1048,7 +1048,8 @@ static int c_cookie(struct conn *c,
     info("S%x %s connected with cookie from %s", c->tag, user, host);
     c->rights |= RIGHT__LOCAL;
   }
-  sink_writes(ev_writer_sink(c->w), "230 OK\n");
+  /* Response contains username so client knows who they are acting as */
+  sink_printf(ev_writer_sink(c->w), "232 %s\n", quoteutf8(user));
   return 1;
 }