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
 \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
 .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
 /*
  * 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
  *
  * 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 "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 sockaddr_un su;
-  struct addrinfo *res;
+  struct addrinfo *res = 0;
   char *name;
   char *name;
-  int n;
+  socklen_t len;
    
   static const struct addrinfo pref = {
     0,
    
   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;
   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 {
   } 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;
       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
 
 #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 */
 
 
 #endif /* CLIENT_COMMON_H */
 
index 0c12c90167aed0efa160812111c1d42bc7d7e793..fcb0dba6740ac0774b3a0ebab8c77011bb72217d 100644 (file)
@@ -66,19 +66,13 @@ struct disorder_client {
   int verbose;
 };
 
   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
  *
 /** @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));
  */
 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;
 }
 
   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 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
  *
  * @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;
   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;
     return -1;
-  }
   c->fpin = c->fpout = 0;
   if((fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) {
     error(errno, "error calling socket");
     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;
   }
     error(errno, "error calling connect");
     goto error;
   }
@@ -287,7 +271,6 @@ static int disorder_connect_sock(disorder_client *c,
     goto error;
   }
   fd2 = -1;
     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)))
   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;
   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;
   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;
 }
 
   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
 /** @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->fpout = 0;
   }
+  c->ident = 0;
+  c->user = 0;
   return 0;
 }
 
   return 0;
 }
 
@@ -431,26 +477,6 @@ int disorder_rescan(disorder_client *c) {
   return disorder_simple(c, 0, "rescan", (char *)0);
 }
 
   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)
 /** @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
 
 /** @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
  * @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
 
 /** @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
  * @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
 
 /** @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
  * @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);
 
 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);
 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 {
 
 /** @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 */
   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 ******************************************************/
 
 
 /* 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 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);
 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;
     }
       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. */
     /* 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 */
 }
 
 /** @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"));
 
   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;
   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 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;
     }
   } 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;
   }
     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;
 }
 
   return 1;
 }