int server_connection_create(struct doveadm_server *server, struct server_connection **conn_r) { #define DOVEADM_SERVER_HANDSHAKE "VERSION\tdoveadm-server\t1\t0\n" struct server_connection *conn; pool_t pool; pool = pool_alloconly_create("doveadm server connection", 1024*16); conn = p_new(pool, struct server_connection, 1); conn->pool = pool; conn->server = server; conn->fd = doveadm_connect_with_default_port(server->name, doveadm_settings->doveadm_port); net_set_nonblock(conn->fd, TRUE); conn->io = io_add(conn->fd, IO_READ, server_connection_input, conn); conn->input = i_stream_create_fd(conn->fd, MAX_INBUF_SIZE, FALSE); conn->output = o_stream_create_fd(conn->fd, (size_t)-1, FALSE); array_append(&conn->server->connections, &conn, 1); if (server_connection_read_settings(conn) < 0 || server_connection_init_ssl(conn) < 0) { server_connection_destroy(&conn); return -1; } o_stream_set_no_error_handling(conn->output, TRUE); conn->state = SERVER_REPLY_STATE_DONE; o_stream_nsend_str(conn->output, DOVEADM_SERVER_HANDSHAKE); *conn_r = conn; return 0; }
static int server_connection_output(struct server_connection *conn) { int ret; ret = o_stream_flush(conn->output); if (ret > 0 && conn->cmd_input != NULL && conn->delayed_cmd == NULL) ret = server_connection_send_cmd_input_more(conn); if (ret < 0) server_connection_destroy(&conn); return ret; }
static void server_handle_input(struct server_connection *conn, const unsigned char *data, size_t size) { string_t *str; size_t i, start; if (printing_conn == conn) { /* continue printing */ } else if (printing_conn == NULL) { printing_conn = conn; } else { /* someone else is printing. don't continue until it goes away */ io_remove(&conn->io); return; } if (data[size-1] == '\001') { /* last character is an escape */ size--; } str = t_str_new(128); for (i = start = 0; i < size; i++) { if (data[i] == '\n') { if (i != start) { i_error("doveadm server sent broken print input"); server_connection_destroy(&conn); return; } conn->state = SERVER_REPLY_STATE_RET; i_stream_skip(conn->input, i + 1); print_connection_released(); return; } if (data[i] == '\t') { server_flush_field(conn, str, data + start, i - start); start = i + 1; } } if (start != size) { conn->streaming = TRUE; stream_data(str, data + start, size - start); } i_stream_skip(conn->input, size); }
static void server_connection_input(struct server_connection *conn) { const unsigned char *data; size_t size; const char *line; int exit_code; if (!conn->handshaked) { if ((line = i_stream_read_next_line(conn->input)) == NULL) { if (conn->input->eof || conn->input->stream_errno != 0) { server_log_disconnect_error(conn); server_connection_destroy(&conn); } return; } conn->handshaked = TRUE; if (strcmp(line, "+") == 0) server_connection_authenticated(conn); else if (strcmp(line, "-") == 0) { if (server_connection_authenticate(conn) < 0) { server_connection_destroy(&conn); return; } return; } else { i_error("doveadm server sent invalid handshake: %s", line); server_connection_destroy(&conn); return; } } if (i_stream_read(conn->input) < 0) { /* disconnected */ server_log_disconnect_error(conn); server_connection_destroy(&conn); return; } if (!conn->authenticated) { if ((line = i_stream_next_line(conn->input)) == NULL) return; if (strcmp(line, "+") == 0) server_connection_authenticated(conn); else { i_error("doveadm authentication failed (%s)", line+1); server_connection_destroy(&conn); return; } } data = i_stream_get_data(conn->input, &size); if (size == 0) return; switch (conn->state) { case SERVER_REPLY_STATE_DONE: i_error("doveadm server sent unexpected input"); server_connection_destroy(&conn); return; case SERVER_REPLY_STATE_PRINT: server_handle_input(conn, data, size); if (conn->state != SERVER_REPLY_STATE_RET) break; /* fall through */ case SERVER_REPLY_STATE_RET: line = i_stream_next_line(conn->input); if (line == NULL) return; if (line[0] == '+') server_connection_callback(conn, 0, ""); else if (line[0] == '-') { line++; if (strcmp(line, "NOUSER") == 0) exit_code = EX_NOUSER; else if (str_to_int(line, &exit_code) < 0) { /* old doveadm-server */ exit_code = EX_TEMPFAIL; } server_connection_callback(conn, exit_code, line); } else { i_error("doveadm server sent broken input " "(expected cmd reply): %s", line); server_connection_destroy(&conn); break; } if (conn->callback == NULL) { /* we're finished, close the connection */ server_connection_destroy(&conn); } break; } }
static int dsync_connect_tcp(struct dsync_cmd_context *ctx, const struct mail_storage_settings *mail_set, const char *target, bool ssl, const char **error_r) { struct doveadm_server *server; struct server_connection *conn; struct ioloop *ioloop; string_t *cmd; const char *error; server = p_new(ctx->ctx.pool, struct doveadm_server, 1); server->name = p_strdup(ctx->ctx.pool, target); if (ssl) { if (dsync_init_ssl_ctx(ctx, mail_set, &error) < 0) { *error_r = t_strdup_printf( "Couldn't initialize SSL context: %s", error); return -1; } server->ssl_ctx = ctx->ssl_ctx; } p_array_init(&server->connections, ctx->ctx.pool, 1); p_array_init(&server->queue, ctx->ctx.pool, 1); ioloop = io_loop_create(); if (server_connection_create(server, &conn) < 0) { *error_r = "Couldn't create server connection"; return -1; } /* <flags> <username> <command> [<args>] */ cmd = t_str_new(256); if (doveadm_debug) str_append_c(cmd, 'D'); str_append_c(cmd, '\t'); str_append_tabescaped(cmd, ctx->ctx.cur_username); str_append(cmd, "\tdsync-server\t-u"); str_append_tabescaped(cmd, ctx->ctx.cur_username); if (ctx->replicator_notify) str_append(cmd, "\t-U"); str_append_c(cmd, '\n'); ctx->tcp_conn = conn; server_connection_cmd(conn, str_c(cmd), dsync_connected_callback, ctx); io_loop_run(ioloop); ctx->tcp_conn = NULL; if (array_count(&server->connections) > 0) server_connection_destroy(&conn); io_loop_destroy(&ioloop); if (ctx->error != NULL) { *error_r = ctx->error; ctx->error = NULL; return -1; } ctx->run_type = DSYNC_RUN_TYPE_STREAM; return 0; }