static void master_connection_input(struct master_connection *conn) { const char *line; int ret; if (i_stream_read(conn->input) < 0) { master_service_stop(master_service); return; } if (!conn->version_received) { if ((line = i_stream_next_line(conn->input)) == NULL) return; if (!version_string_verify(line, INDEXER_MASTER_NAME, INDEXER_PROTOCOL_MAJOR_VERSION)) { i_error("Indexer master not compatible with this master " "(mixed old and new binaries?)"); master_service_stop(master_service); return; } conn->version_received = TRUE; } while ((line = i_stream_next_line(conn->input)) != NULL) { T_BEGIN { ret = master_connection_input_line(conn, line); } T_END; if (ret < 0) { master_service_stop(master_service); break; } } }
static void proxy_input(struct client *client) { struct istream *input; struct ostream *output; const char *line; unsigned int duration; if (client->login_proxy == NULL) { /* we're just freeing the proxy */ return; } input = login_proxy_get_istream(client->login_proxy); if (input == NULL) { if (client->destroyed) { /* we came here from client_destroy() */ return; } /* failed for some reason, probably server disconnected */ client_proxy_failed(client, TRUE); return; } i_assert(!client->destroyed); switch (i_stream_read(input)) { case -2: client_log_err(client, "proxy: Remote input buffer full"); client_proxy_failed(client, TRUE); return; case -1: line = i_stream_next_line(input); duration = ioloop_time - client->created; client_log_err(client, t_strdup_printf( "proxy: Remote %s:%u disconnected: %s " "(state=%u, duration=%us)%s", login_proxy_get_host(client->login_proxy), login_proxy_get_port(client->login_proxy), get_disconnect_reason(input), client->proxy_state, duration, line == NULL ? "" : t_strdup_printf( " - BUG: line not read: %s", line))); client_proxy_failed(client, TRUE); return; } output = client->output; o_stream_ref(output); o_stream_cork(output); while ((line = i_stream_next_line(input)) != NULL) { if (client->v.proxy_parse_line(client, line) != 0) break; } o_stream_uncork(output); o_stream_unref(&output); }
static void auth_server_connection_input(struct auth_server_connection *conn) { struct istream *input; const char *line, *error; int ret; switch (i_stream_read(conn->input)) { case 0: return; case -1: /* disconnected */ error = conn->input->stream_errno != 0 ? strerror(conn->input->stream_errno) : "EOF"; auth_server_connection_reconnect(conn, error); return; case -2: /* buffer full - can't happen unless auth is buggy */ i_error("BUG: Auth server sent us more than %d bytes of data", AUTH_SERVER_CONN_MAX_LINE_LENGTH); auth_server_connection_disconnect(conn, "buffer full"); return; } if (!conn->version_received) { line = i_stream_next_line(conn->input); if (line == NULL) return; /* make sure the major version matches */ if (strncmp(line, "VERSION\t", 8) != 0 || !str_uint_equals(t_strcut(line + 8, '\t'), AUTH_CLIENT_PROTOCOL_MAJOR_VERSION)) { i_error("Authentication server not compatible with " "this client (mixed old and new binaries?)"); auth_server_connection_disconnect(conn, "incompatible server"); return; } conn->version_received = TRUE; } input = conn->input; i_stream_ref(input); while ((line = i_stream_next_line(input)) != NULL && !input->closed) { T_BEGIN { ret = auth_server_connection_input_line(conn, line); } T_END; if (ret < 0) { auth_server_connection_disconnect(conn, t_strdup_printf( "Received broken input: %s", line)); break; } } i_stream_unref(&input); }
void connection_input_default(struct connection *conn) { const char *line; struct istream *input; int ret = 0; switch (connection_input_read(conn)) { case -1: return; case 0: /* allow calling this function for buffered input */ case 1: break; default: i_unreached(); } input = conn->input; i_stream_ref(input); while (!input->closed && (line = i_stream_next_line(input)) != NULL) { T_BEGIN { ret = conn->list->v.input_line(conn, line); } T_END; if (ret <= 0) break; } if (ret < 0 && !input->closed) { conn->disconnect_reason = CONNECTION_DISCONNECT_DEINIT; conn->list->v.destroy(conn); } i_stream_unref(&input); }
static void postfix_input(struct auth_postfix_connection *conn) { char *line; bool ret; switch (i_stream_read(conn->input)) { case 0: return; case -1: /* disconnected */ auth_postfix_connection_destroy(&conn); return; case -2: /* buffer full */ i_error("BUG: Postfix sent us more than %d bytes", (int)MAX_INBUF_SIZE); auth_postfix_connection_destroy(&conn); return; } while ((line = i_stream_next_line(conn->input)) != NULL) { T_BEGIN { ret = auth_postfix_input_line(conn, line); } T_END; if (!ret) { auth_postfix_connection_destroy(&conn); return; } } }
static void notify_input(struct notify_connection *conn) { const char *line; int ret; switch (i_stream_read(conn->input)) { case -2: /* buffer full */ i_error("Client sent too long line"); (void)notify_input_error(conn); return; case -1: /* disconnected */ notify_connection_destroy(conn); return; } while ((line = i_stream_next_line(conn->input)) != NULL) { T_BEGIN { ret = notify_input_line(conn, line); } T_END; if (ret < 0) { if (!notify_input_error(conn)) return; } } }
char *i_stream_read_next_line(struct istream *stream) { char *line; for (;;) { line = i_stream_next_line(stream); if (line != NULL) break; switch (i_stream_read(stream)) { case -2: io_stream_set_error(&stream->real_stream->iostream, "Line is too long (over %"PRIuSIZE_T " bytes at offset %"PRIuUOFF_T")", i_stream_get_data_size(stream), stream->v_offset); stream->stream_errno = errno = ENOBUFS; stream->eof = TRUE; return NULL; case -1: return i_stream_last_line(stream->real_stream); case 0: return NULL; } } return line; }
static const char *next_line(struct mailbox_list *list, const char *path, struct istream *input, bool *failed_r, bool ignore_estale) { const char *line; *failed_r = FALSE; while ((line = i_stream_next_line(input)) == NULL) { switch (i_stream_read(input)) { case -1: if (input->stream_errno != 0 && (input->stream_errno != ESTALE || !ignore_estale)) { subswrite_set_syscall_error(list, "read()", path); *failed_r = TRUE; } return NULL; case -2: /* mailbox name too large */ mailbox_list_set_critical(list, "Subscription file %s contains lines longer " "than %u characters", path, (unsigned int)list->mailbox_name_max_length); *failed_r = TRUE; return NULL; } } return line; }
static bool client_handle_input(struct client *client) { const char *line, *cmd, *error; int ret; if (client->url != NULL || client->to_delay != NULL) { /* we're still processing a URL. wait until it's finished. */ io_remove(&client->io); client->io = NULL; client->waiting_input = TRUE; return TRUE; } if (client->io == NULL) { client->io = io_add(client->fd_in, IO_READ, client_input, client); } client->waiting_input = FALSE; timeout_reset(client->to_idle); switch (i_stream_read(client->input)) { case -1: /* disconnected */ if (client->ctrl_output != NULL) (void)o_stream_send_str(client->ctrl_output, "DISCONNECTED\n"); client_destroy(client); return FALSE; case -2: /* line too long, kill it */ client_abort(client, "Session aborted: Input line too long"); return FALSE; } while ((line = i_stream_next_line(client->input)) != NULL) { const char *const *args = t_strsplit_tabescaped(line); if (args[0] == NULL) continue; cmd = args[0]; args++; if (client->mail_user == NULL) ret = client_handle_user_command(client, cmd, args, &error); else ret = client_handle_command(client, cmd, args, &error); if (ret <= 0) { if (ret == 0) break; i_error("Client input error: %s", error); client_abort(client, "Session aborted: Unexpected input"); return FALSE; } } return TRUE; }
static void worker_connection_input(struct worker_connection *conn) { const char *line; if (i_stream_read(conn->input) < 0) { worker_connection_disconnect(conn); return; } if (!conn->version_received) { if ((line = i_stream_next_line(conn->input)) == NULL) return; if (!version_string_verify(line, INDEXER_WORKER_NAME, INDEXER_PROTOCOL_MAJOR_VERSION)) { i_error("Indexer worker not compatible with this master " "(mixed old and new binaries?)"); worker_connection_disconnect(conn); return; } conn->version_received = TRUE; } if (conn->process_limit == 0) { if ((line = i_stream_next_line(conn->input)) == NULL) return; if (str_to_uint(line, &conn->process_limit) < 0 || conn->process_limit == 0) { i_error("Indexer worker sent invalid handshake: %s", line); worker_connection_disconnect(conn); return; } } while ((line = i_stream_next_line(conn->input)) != NULL) { if (worker_connection_input_line(conn, line) < 0) { worker_connection_disconnect(conn); break; } } }
static void notify_connection_input(struct notify_connection *conn) { const char *line; int ret; switch (i_stream_read(conn->input)) { case -2: i_error("BUG: Client connection sent too much data"); notify_connection_destroy(conn); return; case -1: notify_connection_destroy(conn); return; } if (!conn->version_received) { if ((line = i_stream_next_line(conn->input)) == NULL) return; if (!version_string_verify(line, "replicator-notify", NOTIFY_CLIENT_PROTOCOL_MAJOR_VERSION)) { i_error("Notify client not compatible with this server " "(mixed old and new binaries?)"); notify_connection_destroy(conn); return; } conn->version_received = TRUE; } while ((line = i_stream_next_line(conn->input)) != NULL) { T_BEGIN { ret = notify_connection_input_line(conn, line); } T_END; if (ret < 0) { notify_connection_destroy(conn); break; } } }
static int config_read_reply_header(struct istream *istream, const char *path, pool_t pool, const struct master_service_settings_input *input, struct master_service_settings_output *output_r, const char **error_r) { const char *line; ssize_t ret; while ((ret = i_stream_read(istream)) > 0) { line = i_stream_next_line(istream); if (line != NULL) break; } if (ret <= 0) { if (ret == 0) return 1; *error_r = istream->stream_errno != 0 ? t_strdup_printf("read(%s) failed: %s", path, i_stream_get_error(istream)) : t_strdup_printf("read(%s) failed: EOF", path); return -1; } T_BEGIN { const char *const *arg = t_strsplit_tabescaped(line); ARRAY_TYPE(const_string) services; p_array_init(&services, pool, 8); for (; *arg != NULL; arg++) { if (strcmp(*arg, "service-uses-local") == 0) output_r->service_uses_local = TRUE; else if (strcmp(*arg, "service-uses-remote") == 0) output_r->service_uses_remote = TRUE; if (strcmp(*arg, "used-local") == 0) output_r->used_local = TRUE; else if (strcmp(*arg, "used-remote") == 0) output_r->used_remote = TRUE; else if (strncmp(*arg, "service=", 8) == 0) { const char *name = p_strdup(pool, *arg + 8); array_append(&services, &name, 1); } } if (input->service == NULL) { array_append_zero(&services); output_r->specific_services = array_idx(&services, 0); } } T_END; return 0; }
static bool idle_client_handle_input(struct cmd_idle_context *ctx, bool free_cmd) { const char *line; while ((line = i_stream_next_line(ctx->client->input)) != NULL) { if (ctx->client->input_skip_line) ctx->client->input_skip_line = FALSE; else { idle_finish(ctx, strcasecmp(line, "DONE") == 0, free_cmd); return TRUE; } } return FALSE; }
static void replicator_input(struct replicator_connection *conn) { const char *line; switch (i_stream_read(conn->input)) { case -2: /* buffer full */ i_error("Replicator sent too long line"); replicator_connection_disconnect(conn); return; case -1: /* disconnected */ replicator_connection_disconnect(conn); return; } while ((line = i_stream_next_line(conn->input)) != NULL) (void)replicator_input_line(conn, line); }
static void fifo_input_connection_input(struct fifo_input_connection *conn) { const char *line, *const *args, *error; switch (i_stream_read(conn->input)) { case -2: i_error("BUG: Mail server sent too much data"); fifo_input_connection_destroy(&conn); return; case -1: fifo_input_connection_destroy(&conn); return; } while ((line = i_stream_next_line(conn->input)) != NULL) T_BEGIN { args = t_strsplit_tabescaped(line); if (fifo_input_connection_request(args, &error) < 0) i_error("FIFO input error: %s", error); } T_END; }
static void remote_error_input(struct dsync_cmd_context *ctx) { const unsigned char *data; size_t size; const char *line; switch (i_stream_read(ctx->err_stream)) { case -2: data = i_stream_get_data(ctx->err_stream, &size); fprintf(stderr, "%.*s", (int)size, data); i_stream_skip(ctx->err_stream, size); break; case -1: if (ctx->io_err != NULL) io_remove(&ctx->io_err); break; default: while ((line = i_stream_next_line(ctx->err_stream)) != NULL) fprintf(stderr, "%s\n", line); break; } }
char *i_stream_read_next_line(struct istream *stream) { char *line; for (;;) { line = i_stream_next_line(stream); if (line != NULL) break; switch (i_stream_read(stream)) { case -2: stream->stream_errno = ENOBUFS; stream->eof = TRUE; return NULL; case -1: return i_stream_last_line(stream->real_stream); case 0: return NULL; } } return line; }
static void config_connection_input(void *context) { struct config_connection *conn = context; const char *const *args, *line; switch (i_stream_read(conn->input)) { case -2: i_error("BUG: Config client connection sent too much data"); config_connection_destroy(conn); return; case -1: config_connection_destroy(conn); return; } if (!conn->version_received) { line = i_stream_next_line(conn->input); if (line == NULL) return; if (!version_string_verify(line, "config", CONFIG_CLIENT_PROTOCOL_MAJOR_VERSION)) { i_error("Config client not compatible with this server " "(mixed old and new binaries?)"); config_connection_destroy(conn); return; } conn->version_received = TRUE; } while ((args = config_connection_next_line(conn)) != NULL) { if (args[0] == NULL) continue; if (strcmp(args[0], "REQ") == 0) { if (config_connection_request(conn, args + 1) < 0) break; } } }
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 void auth_client_input(struct auth_client_connection *conn) { char *line; bool ret; switch (i_stream_read(conn->input)) { case 0: return; case -1: /* disconnected */ auth_client_disconnected(&conn); return; case -2: /* buffer full */ i_error("BUG: Auth client %u sent us more than %d bytes", conn->pid, (int)AUTH_CLIENT_MAX_LINE_LENGTH); auth_client_connection_destroy(&conn); return; } while (conn->request_handler == NULL) { /* still handshaking */ line = i_stream_next_line(conn->input); if (line == NULL) return; if (!conn->version_received) { /* make sure the major version matches */ if (strncmp(line, "VERSION\t", 8) != 0 || !str_uint_equals(t_strcut(line + 8, '\t'), AUTH_CLIENT_PROTOCOL_MAJOR_VERSION)) { i_error("Authentication client " "not compatible with this server " "(mixed old and new binaries?)"); auth_client_connection_destroy(&conn); return; } conn->version_received = TRUE; continue; } if (strncmp(line, "CPID\t", 5) == 0) { if (!auth_client_input_cpid(conn, line + 5)) { auth_client_connection_destroy(&conn); return; } } else { i_error("BUG: Authentication client sent " "unknown handshake command: %s", str_sanitize(line, 80)); auth_client_connection_destroy(&conn); return; } } conn->refcount++; while ((line = i_stream_next_line(conn->input)) != NULL) { T_BEGIN { ret = auth_client_handle_line(conn, line); safe_memset(line, 0, strlen(line)); } T_END; if (!ret) { struct auth_client_connection *tmp_conn = conn; auth_client_connection_destroy(&tmp_conn); break; } } auth_client_connection_unref(&conn); }
static void client_ctrl_input(struct client *client) { const char *const *args; const char *line; int ret; timeout_reset(client->to_idle); if (client->fd_in == -1 || client->fd_out == -1) { if ((ret = client_ctrl_read_fds(client)) <= 0) { if (ret < 0) client_abort(client, "FD Transfer failed"); return; } } switch (i_stream_read(client->ctrl_input)) { case -1: /* disconnected */ client_destroy(client); return; case -2: /* line too long, kill it */ client_abort(client, "Control session aborted: Input line too long"); return; } if (!client->version_received) { if ((line = i_stream_next_line(client->ctrl_input)) == NULL) return; if (!version_string_verify(line, "imap-urlauth-worker", IMAP_URLAUTH_WORKER_PROTOCOL_MAJOR_VERSION)) { i_error("imap-urlauth-worker client not compatible with this server " "(mixed old and new binaries?) %s", line); client_abort(client, "Control session aborted: Version mismatch"); return; } client->version_received = TRUE; if (o_stream_send_str(client->ctrl_output, "OK\n") < 0) { client_destroy(client); return; } } if (client->access_received) { client_abort(client, "Control session aborted: Unexpected input"); return; } if ((line = i_stream_next_line(client->ctrl_input)) == NULL) return; args = t_strsplit_tabescaped(line); if (*args == NULL || strcmp(*args, "ACCESS") != 0) { i_error("Invalid control command: %s", str_sanitize(line, 80)); client_abort(client, "Control session aborted: Invalid command"); return; } args++; if (*args == NULL) { i_error("Invalid ACCESS command: %s", str_sanitize(line, 80)); client_abort(client, "Control session aborted: Invalid command"); return; } i_assert(client->access_user == NULL); if (**args != '\0') { client->access_user = i_strdup(*args); client->access_anonymous = FALSE; } else { client->access_user = i_strdup("anonymous"); client->access_anonymous = TRUE; } i_set_failure_prefix("imap-urlauth[%s](%s): ", my_pid, client->access_user); args++; while (*args != NULL) { /* debug */ if (strcasecmp(*args, "debug") == 0) { client->debug = TRUE; /* apps=<access-application>[,<access-application,...] */ } else if (strncasecmp(*args, "apps=", 5) == 0 && (*args)[5] != '\0') { const char *const *apps = t_strsplit(*args+5, ","); while (*apps != NULL) { char *app = i_strdup(*apps); array_append(&client->access_apps, &app, 1); if (client->debug) { i_debug("User %s has URLAUTH %s access", client->access_user, app); } apps++; } } else { i_error("Invalid ACCESS parameter: %s", str_sanitize(*args, 80)); client_abort(client, "Control session aborted: Invalid command"); return; } args++; } client->access_received = TRUE; if (o_stream_send_str(client->ctrl_output, "OK\n") < 0) { client_destroy(client); return; } client->input = i_stream_create_fd(client->fd_in, MAX_INBUF_SIZE, FALSE); client->output = o_stream_create_fd(client->fd_out, (size_t)-1, FALSE); client->io = io_add(client->fd_in, IO_READ, client_input, client); o_stream_set_flush_callback(client->output, client_output, client); if (client->debug) { i_debug("Worker activated for access by user %s", client->access_user); } }
static void master_login_auth_input(struct master_login_auth *auth) { const char *line; bool ret; switch (i_stream_read(auth->input)) { case 0: return; case -1: /* disconnected. stop accepting new connections, because in default configuration we no longer have permissions to connect back to auth-master */ master_service_stop_new_connections(master_service); master_login_auth_disconnect(auth); return; case -2: /* buffer full */ i_error("Auth server sent us too long line"); master_login_auth_disconnect(auth); return; } if (!auth->version_received) { line = i_stream_next_line(auth->input); if (line == NULL) return; /* make sure the major version matches */ if (strncmp(line, "VERSION\t", 8) != 0 || !str_uint_equals(t_strcut(line + 8, '\t'), AUTH_MASTER_PROTOCOL_MAJOR_VERSION)) { i_error("Authentication server not compatible with " "master process (mixed old and new binaries?)"); master_login_auth_disconnect(auth); return; } auth->version_received = TRUE; } if (!auth->spid_received) { line = i_stream_next_line(auth->input); if (line == NULL) return; if (strncmp(line, "SPID\t", 5) != 0 || str_to_pid(line + 5, &auth->auth_server_pid) < 0) { i_error("Authentication server didn't " "send valid SPID as expected: %s", line); master_login_auth_disconnect(auth); return; } auth->spid_received = TRUE; master_login_auth_check_spids(auth); } auth->refcount++; while ((line = i_stream_next_line(auth->input)) != NULL) { if (strncmp(line, "USER\t", 5) == 0) ret = master_login_auth_input_user(auth, line + 5); else if (strncmp(line, "NOTFOUND\t", 9) == 0) ret = master_login_auth_input_notfound(auth, line + 9); else if (strncmp(line, "FAIL\t", 5) == 0) ret = master_login_auth_input_fail(auth, line + 5); else ret = TRUE; if (!ret || auth->input == NULL) { master_login_auth_disconnect(auth); break; } } master_login_auth_unref(&auth); }
static void auth_worker_input(struct auth_worker_client *client) { char *line; bool ret; switch (i_stream_read(client->input)) { case 0: return; case -1: /* disconnected */ auth_worker_client_destroy(&client); return; case -2: /* buffer full */ i_error("BUG: Auth worker server sent us more than %d bytes", (int)AUTH_WORKER_MAX_LINE_LENGTH); auth_worker_client_destroy(&client); return; } if (!client->version_received) { line = i_stream_next_line(client->input); if (line == NULL) return; if (!version_string_verify(line, "auth-worker", AUTH_WORKER_PROTOCOL_MAJOR_VERSION)) { i_error("Auth worker not compatible with this server " "(mixed old and new binaries?)"); auth_worker_client_destroy(&client); return; } client->version_received = TRUE; } if (!client->dbhash_received) { line = i_stream_next_line(client->input); if (line == NULL) return; if (!auth_worker_verify_db_hash(line)) { i_error("Auth worker sees different passdbs/userdbs " "than auth server. Maybe config just changed " "and this goes away automatically?"); auth_worker_client_destroy(&client); return; } client->dbhash_received = TRUE; } client->refcount++; while ((line = i_stream_next_line(client->input)) != NULL) { T_BEGIN { ret = auth_worker_handle_line(client, line); } T_END; if (!ret) { struct auth_worker_client *client2 = client; auth_worker_client_destroy(&client2); break; } } auth_worker_client_unref(&client); }