/*
* 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,
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;
}
/*
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));
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;
}
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)))
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;
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
}
c->fpout = 0;
}
+ c->ident = 0;
+ c->user = 0;
return 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 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
/** @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
/** @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
/** @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 */
/* 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);
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. */
}
/** @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;
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;