#include <assert.h>
#include <inttypes.h>
#include <stddef.h>
+#include <time.h>
#include "log.h"
#include "mem.h"
/* TODO: more commands */
+/** @brief How often to send data to the server when receiving logs */
+#define LOG_PROD_INTERVAL 10
+
/* Types *********************************************************************/
/** @brief Client state */
/** @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 rc; /**< @brief response code */
char *line; /**< @brief complete line */
struct vector vec; /**< @brief body */
- const disorder_eclient_log_callbacks *log_callbacks; /**< @brief log callbacks */
+
+ const disorder_eclient_log_callbacks *log_callbacks;
+ /**< @brief log callbacks
+ *
+ * Once disorder_eclient_log() has been issued this is always set. When we
+ * re-connect it is checked to re-issue the log command.
+ */
void *log_v; /**< @brief user data */
unsigned long statebits; /**< @brief latest state */
+
+ time_t last_prod;
+ /**< @brief last time we sent a prod
+ *
+ * When we are receiving log data we send a "prod" byte to the server from
+ * time to time so that we detect broken connections reasonably quickly. The
+ * server just ignores these bytes.
+ */
};
/* 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);
vector_init(&c->vec);
dynstr_init(&c->input);
dynstr_init(&c->output);
- if(!config->password) {
- error(0, "no password set");
- return 0;
- }
return c;
}
*/
void disorder_eclient_polled(disorder_eclient *c, unsigned mode) {
struct operation *op;
+ time_t now;
D(("disorder_eclient_polled fd=%d state=%s mode=[%s %s]",
c->fd, states[c->state],
if(c->state == state_disconnected) {
D(("state_disconnected"));
- with_sockaddr(c, start_connect);
+ /* If there is no password yet then we cannot connect */
+ if(!config->password) {
+ comms_error(c, "no password is configured");
+ return;
+ }
+ 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. */
c->callbacks->report(c->u, 0);
}
+ /* Queue up a byte to send */
+ if(c->state == state_log
+ && c->output.nvec == 0
+ && time(&now) - c->last_prod > LOG_PROD_INTERVAL) {
+ put(c, "x", 1);
+ c->last_prod = now;
+ }
+
if(c->state == state_cmdresponse
|| c->state == state_body
|| c->state == state_log) {
}
/** @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 comms_error(c, "socket: %s", strerror(errno));
c->eof = 0;
nonblock(c->fd);
+ cloexec(c->fd);
if(connect(c->fd, sa, len) < 0) {
switch(errno) {
case EINTR:
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;
const char *res;
char **rvec;
int nrvec;
- const char *algo = "SHA1";
+ const char *protocol, *algorithm, *challenge;
D(("authbanner_opcallback"));
if(c->rc / 100 != 2
disorder_eclient_close(c);
return;
}
- if(nrvec > 1) {
- algo = *rvec++;
- --nrvec;
+ if(nrvec != 3) {
+ protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line);
+ disorder_eclient_close(c);
+ }
+ protocol = *rvec++;
+ algorithm = *rvec++;
+ challenge = *rvec++;
+ if(strcmp(protocol, "2")) {
+ protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line);
+ disorder_eclient_close(c);
}
- nonce = unhex(rvec[0], &nonce_len);
- res = authhash(nonce, nonce_len, config->password, algo);
+ nonce = unhex(challenge, &nonce_len);
+ res = authhash(nonce, nonce_len, config->password, algorithm);
if(!res) {
protocol_error(c, op, c->rc, "%s: unknown authentication algorithm '%s'",
- c->ident, algo);
+ c->ident, algorithm);
disorder_eclient_close(c);
return;
}
/* Command support ***********************************************************/
-/* for commands with a simple string response */
+/* for commands with a quoted string response */
static void string_response_opcallback(disorder_eclient *c,
struct operation *op) {
D(("string_response_callback"));
if(c->rc / 100 == 2) {
- if(op->completed)
- ((disorder_eclient_string_response *)op->completed)(op->v, c->line + 4);
+ if(op->completed) {
+ char **rr = split(c->line + 4, 0, SPLIT_QUOTES, 0, 0);
+
+ if(rr && *rr)
+ ((disorder_eclient_string_response *)op->completed)(op->v, *rr);
+ else
+ protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line);
+ }
} else
protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line);
}