static int get_ftp_response(struct connection *c, struct read_buffer *rb, int part) { int l; set_timeout(c); again: for (l = 0; l < rb->len; l++) if (rb->data[l] == 10) { unsigned char *e; long k = strtoul(cast_const_char rb->data, (char **)(void *)&e, 10); if (e != rb->data + 3 || k < 100 || k >= 1000) return -1; if (*e == '-') { int i; for (i = 0; i < rb->len - 5; i++) { if (rb->data[i] == 10 && !memcmp(rb->data+i+1, rb->data, 3) && rb->data[i+4] == ' ') { for (i++; i < rb->len; i++) if (rb->data[i] == 10) goto ok; return 0; } } return 0; ok: l = i; } if (!part && k >= 100 && k < 200) { kill_buffer_data(rb, l + 1); goto again; } if (part == 2) return k; kill_buffer_data(rb, l + 1); return k; } return 0; }
static void smb_got_data(struct socket *socket, struct read_buffer *rb) { int len = rb->length; struct connection *conn = socket->conn; if (len < 0) { abort_connection(conn, connection_state_for_errno(errno)); return; } if (!len) { abort_connection(conn, connection_state(S_OK)); return; } socket->state = SOCKET_END_ONCLOSE; conn->received += len; if (add_fragment(conn->cached, conn->from, rb->data, len) == 1) conn->tries = 0; conn->from += len; kill_buffer_data(rb, len); read_from_socket(socket, rb, connection_state(S_TRANS), smb_got_data); }
void finger_get_response(struct connection *c, struct read_buffer *rb) { struct cache_entry *e; int l; set_timeout(c); if (get_cache_entry(c->url, &e)) { setcstate(c, S_OUT_OF_MEM); abort_connection(c); return; } c->cache = e; if (rb->close == 2) { setcstate(c, S_OK); finger_end_request(c); return; } l = rb->len; if (c->from + l < 0) { setcstate(c, S_LARGE_FILE); abort_connection(c); return; } c->received += l; if (add_fragment(c->cache, c->from, rb->data, l) == 1) c->tries = 0; c->from += l; kill_buffer_data(rb, l); read_from_socket(c, c->sock1, rb, finger_get_response); setcstate(c, S_TRANS); }
static struct connection_state init_nntp_header(struct connection *conn, struct read_buffer *rb) { struct nntp_connection_info *nntp = conn->info; if (!conn->cached) { conn->cached = get_cache_entry(conn->uri); if (!conn->cached) return connection_state(S_OUT_OF_MEM); } else if (conn->cached->head || conn->cached->content_type) { /* If the head is set wipe out the content to be sure */ delete_entry_content(conn->cached); mem_free_set(&conn->cached->head, NULL); } /* XXX: Override any Content-Type line in the header */ mem_free_set(&conn->cached->content_type, stracpy("text/html")); if (!conn->cached->content_type) return connection_state(S_OUT_OF_MEM); switch (nntp->target) { case NNTP_TARGET_ARTICLE_NUMBER: case NNTP_TARGET_MESSAGE_ID: case NNTP_TARGET_GROUP_MESSAGE_ID: { unsigned char *end; end = get_nntp_message_header_end(rb->data, rb->length); if (!end) { /* Redo the whole cache entry thing next time */ return connection_state(S_TRANS); } /* FIXME: Add the NNTP response code line */ conn->cached->head = stracpy("FIXME NNTP response code\r\n"); if (!conn->cached->head) return connection_state(S_OUT_OF_MEM); add_to_strn(&conn->cached->head, rb->data); /* ... and remove it */ conn->received += end - rb->data; kill_buffer_data(rb, end - rb->data); break; } case NNTP_TARGET_ARTICLE_RANGE: case NNTP_TARGET_GROUP: case NNTP_TARGET_GROUPS: case NNTP_TARGET_QUIT: break; } return connection_state(S_OK); }
struct connection_state read_nntp_response_data(struct connection *conn, struct read_buffer *rb) { struct string html; unsigned char *end; struct connection_state state = connection_state(S_TRANS); if (conn->from == 0) { switch (init_nntp_header(conn, rb).basic) { case S_OK: break; case S_OUT_OF_MEM: return connection_state(S_OUT_OF_MEM); case S_TRANS: return connection_state(S_TRANS); default: return connection_state(S_NNTP_ERROR); } } if (!init_string(&html)) return connection_state(S_OUT_OF_MEM); if (conn->from == 0) add_nntp_html_start(&html, conn); while ((end = get_nntp_line_end(rb->data, rb->length))) { unsigned char *line = check_nntp_line(rb->data, end); if (!line) { state = connection_state(S_OK); break; } add_nntp_html_line(&html, conn, line); conn->received += end - rb->data; kill_buffer_data(rb, end - rb->data); } if (!is_in_state(state, S_TRANS)) add_nntp_html_end(&html, conn); add_fragment(conn->cached, conn->from, html.source, html.length); conn->from += html.length; done_string(&html); return state; }
static void smb_got_error(struct socket *socket, struct read_buffer *rb) { int len = rb->length; struct connection *conn = socket->conn; struct connection_state error; if (len < 0) { abort_connection(conn, connection_state_for_errno(errno)); return; } /* There should be free space in the buffer, because * @alloc_read_buffer allocated several kibibytes, and the * child process wrote only an integer and a newline to the * pipe. */ assert(rb->freespace >= 1); if_assert_failed { abort_connection(conn, connection_state(S_INTERNAL)); return; } rb->data[len] = '\0'; switch (rb->data[0]) { case 'S': error = connection_state_for_errno(atoi(rb->data + 1)); break; case 'I': error = connection_state(atoi(rb->data + 1)); break; default: ERROR("malformed error code: %s", rb->data); error = connection_state(S_INTERNAL); break; } kill_buffer_data(rb, len); if (is_system_error(error) && error.syserr == EACCES) prompt_username_pw(conn); else abort_connection(conn, error); }
enum nntp_code get_nntp_response_code(struct connection *conn, struct read_buffer *rb) { struct nntp_connection_info *nntp = conn->info; unsigned char *line = rb->data; unsigned char *end = get_nntp_line_end(rb->data, rb->length); enum nntp_code code; int linelen; if (!end) return NNTP_CODE_NONE; /* Just to be safe NUL terminate the line */ end[-1] = 0; linelen = end - line; if (linelen < sizeof("xxx\r\n") - 1 || !isdigit(line[0]) || !isdigit(line[1]) || !isdigit(line[2]) || isdigit(line[3])) return NNTP_CODE_INVALID; code = atoi(line); if (!check_nntp_code_valid(code)) return NNTP_CODE_INVALID; /* Only when listing all articles in group the parameters is needed */ if (code == NNTP_CODE_211_GROUP_SELECTED && nntp->target == NNTP_TARGET_GROUP && !parse_nntp_group_parameters(nntp, line + 4, end)) return NNTP_CODE_INVALID; /* Remove the response line */ kill_buffer_data(rb, linelen); conn->received += linelen; return code; }
static void finger_get_response(struct connection *c, struct read_buffer *rb) { int l; int a; set_timeout(c); if (!c->cache) { if (get_cache_entry(c->url, &c->cache)) { setcstate(c, S_OUT_OF_MEM); abort_connection(c); return; } c->cache->refcount--; } if (rb->close == 2) { finger_end_request(c, S__OK); return; } l = rb->len; if ((off_t)(0UL + c->from + l) < 0) { setcstate(c, S_LARGE_FILE); abort_connection(c); return; } c->received += l; a = add_fragment(c->cache, c->from, rb->data, l); if (a < 0) { setcstate(c, a); abort_connection(c); return; } if (a == 1) c->tries = 0; c->from += l; kill_buffer_data(rb, l); read_from_socket(c, c->sock1, rb, finger_get_response); setcstate(c, S_TRANS); }
void http_got_header(struct connection *c, struct read_buffer *rb) { int cf; int state = c->state != S_PROC ? S_GETH : S_PROC; unsigned char *head; unsigned char *cookie, *ch; int a, h, version; unsigned char *d; struct cache_entry *e; struct http_connection_info *info; unsigned char *host = upcase(c->url[0]) != 'P' ? c->url : get_url_data(c->url); set_timeout(c); info = c->info; if (rb->close == 2) { unsigned char *h; if (!c->tries && (h = get_host_name(host))) { if (info->bl_flags & BL_NO_CHARSET) { del_blacklist_entry(h, BL_NO_CHARSET); } else { add_blacklist_entry(h, BL_NO_CHARSET); c->tries = -1; } mem_free(h); } setcstate(c, S_CANT_READ); retry_connection(c); return; } rb->close = 0; again: if ((a = get_header(rb)) == -1) { setcstate(c, S_HTTP_ERROR); abort_connection(c); return; } if (!a) { read_from_socket(c, c->sock1, rb, http_got_header); setcstate(c, state); return; } if (a != -2) { head = mem_alloc(a + 1); memcpy(head, rb->data, a); head[a] = 0; kill_buffer_data(rb, a); } else { head = stracpy("HTTP/0.9 200 OK\r\nContent-Type: text/html\r\n\r\n"); } if (get_http_code(head, &h, &version) || h == 101) { mem_free(head); setcstate(c, S_HTTP_ERROR); abort_connection(c); return; } if (check_http_server_bugs(host, c->info, head) && is_connection_restartable(c)) { mem_free(head); setcstate(c, S_RESTART); retry_connection(c); return; } ch = head; while ((cookie = parse_http_header(ch, "Set-Cookie", &ch))) { unsigned char *host = upcase(c->url[0]) != 'P' ? c->url : get_url_data(c->url); set_cookie(NULL, host, cookie); mem_free(cookie); } if (h == 100) { mem_free(head); state = S_PROC; goto again; } if (h < 200) { mem_free(head); setcstate(c, S_HTTP_ERROR); abort_connection(c); return; } if (h == 204) { mem_free(head); setcstate(c, S_HTTP_204); http_end_request(c, 0); return; } if (h == 304) { mem_free(head); setcstate(c, S_OK); http_end_request(c, 1); return; } if ((h == 500 || h == 502 || h == 503 || h == 504) && http_bugs.retry_internal_errors && is_connection_restartable(c)) { /* !!! FIXME: wait some time ... */ mem_free(head); setcstate(c, S_RESTART); retry_connection(c); return; } if (!c->cache && get_cache_entry(c->url, &c->cache)) { mem_free(head); setcstate(c, S_OUT_OF_MEM); abort_connection(c); return; } e = c->cache; e->http_code = h; if (e->head) mem_free(e->head); e->head = head; if ((d = parse_http_header(head, "Expires", NULL))) { time_t t = parse_http_date(d); if (t && e->expire_time != 1) e->expire_time = t; mem_free(d); } if ((d = parse_http_header(head, "Pragma", NULL))) { if (!casecmp(d, "no-cache", 8)) e->expire_time = 1; mem_free(d); } if ((d = parse_http_header(head, "Cache-Control", NULL))) { char *f = d; while (1) { while (*f && (*f == ' ' || *f == ',')) f++; if (!*f) break; if (!casecmp(f, "no-cache", 8) || !casecmp(f, "must-revalidate", 15)) { e->expire_time = 1; } if (!casecmp(f, "max-age=", 8)) { if (e->expire_time != 1) e->expire_time = time(NULL) + atoi(f + 8); } while (*f && *f != ',') f++; } mem_free(d); } #ifdef HAVE_SSL if (c->ssl) { int l = 0; if (e->ssl_info) mem_free(e->ssl_info); e->ssl_info = init_str(); add_num_to_str(&e->ssl_info, &l, SSL_get_cipher_bits(c->ssl, NULL)); add_to_str(&e->ssl_info, &l, "-bit "); add_to_str(&e->ssl_info, &l, SSL_get_cipher_version(c->ssl)); add_to_str(&e->ssl_info, &l, " "); add_to_str(&e->ssl_info, &l, (unsigned char *)SSL_get_cipher_name(c->ssl)); } #endif if (e->redirect) mem_free(e->redirect), e->redirect = NULL; if (h == 301 || h == 302 || h == 303 || h == 307) { if ((h == 302 || h == 307) && !e->expire_time) e->expire_time = 1; if ((d = parse_http_header(e->head, "Location", NULL))) { unsigned char *user, *ins; unsigned char *newuser, *newpassword; if (!parse_url(d, NULL, &user, NULL, NULL, NULL, &ins, NULL, NULL, NULL, NULL, NULL, NULL) && !user && ins && (newuser = get_user_name(host))) { if (*newuser) { int ins_off = ins - d; newpassword = get_pass(host); if (!newpassword) newpassword = stracpy(""); add_to_strn(&newuser, ":"); add_to_strn(&newuser, newpassword); add_to_strn(&newuser, "@"); extend_str(&d, strlen(newuser)); ins = d + ins_off; memmove(ins + strlen(newuser), ins, strlen(ins) + 1); memcpy(ins, newuser, strlen(newuser)); mem_free(newpassword); } mem_free(newuser); } if (e->redirect) mem_free(e->redirect); e->redirect = d; e->redirect_get = h == 303; } } if (!e->expire_time && strchr(c->url, POST_CHAR)) e->expire_time = 1; info->close = 0; info->length = -1; info->version = version; if ((d = parse_http_header(e->head, "Connection", NULL)) || (d = parse_http_header(e->head, "Proxy-Connection", NULL))) { if (!strcasecmp(d, "close")) info->close = 1; mem_free(d); } else if (version < 11) info->close = 1; cf = c->from; c->from = 0; if ((d = parse_http_header(e->head, "Content-Range", NULL))) { if (strlen(d) > 6) { d[5] = 0; if (!(strcasecmp(d, "bytes")) && d[6] >= '0' && d[6] <= '9') { #if defined(HAVE_STRTOLL) long long f = strtoll(d + 6, NULL, 10); #elif defined(HAVE_STRTOQ) longlong f = strtoq(d + 6, NULL, 10); #else long f = strtol(d + 6, NULL, 10); if (f == MAXLONG) f = -1; #endif if (f >= 0 && (off_t)f >= 0 && (off_t)f == f) c->from = f; } } mem_free(d); } if (cf && !c->from && !c->unrestartable) c->unrestartable = 1; if (c->from > cf || c->from < 0) { setcstate(c, S_HTTP_ERROR); abort_connection(c); return; } if ((d = parse_http_header(e->head, "Content-Length", NULL))) { unsigned char *ep; #if defined(HAVE_STRTOLL) long long l = strtoll(d, (char **)(void *)&ep, 10); #elif defined(HAVE_STRTOQ) longlong l = strtoq(d, (char **)(void *)&ep, 10); #else long l = strtol(d, (char **)(void *)&ep, 10); if (l == MAXLONG) l = -1; #endif if (!*ep && l >= 0 && (off_t)l >= 0 && (off_t)l == l) { if (!info->close || version >= 11) info->length = l; if (c->from + l >= 0) c->est_length = c->from + l; } mem_free(d); } if ((d = parse_http_header(e->head, "Accept-Ranges", NULL))) { if (!strcasecmp(d, "none") && !c->unrestartable) c->unrestartable = 1; mem_free(d); } else { if (!c->unrestartable && !c->from) c->unrestartable = 1; } if (info->bl_flags & BL_NO_RANGE && !c->unrestartable) c->unrestartable = 1; if ((d = parse_http_header(e->head, "Transfer-Encoding", NULL))) { if (!strcasecmp(d, "chunked")) { info->length = -2; info->chunk_remaining = -1; } mem_free(d); } if (!info->close && info->length == -1) info->close = 1; if ((d = parse_http_header(e->head, "Last-Modified", NULL))) { if (e->last_modified && strcasecmp(e->last_modified, d)) { delete_entry_content(e); if (c->from) { c->from = 0; mem_free(d); setcstate(c, S_MODIFIED); retry_connection(c); return; } } if (!e->last_modified) e->last_modified = d; else mem_free(d); } if (!e->last_modified && (d = parse_http_header(e->head, "Date", NULL))) e->last_modified = d; if (info->length == -1 || (version < 11 && info->close)) rb->close = 1; read_http_data(c, rb); }
void read_http_data(struct connection *c, struct read_buffer *rb) { struct http_connection_info *info = c->info; set_timeout(c); if (rb->close == 2) { setcstate(c, S_OK); http_end_request(c, 0); return; } if (info->length != -2) { int l = rb->len; if (info->length >= 0 && info->length < l) l = info->length; if (c->from + l < 0) { setcstate(c, S_LARGE_FILE); abort_connection(c); return; } c->received += l; if (add_fragment(c->cache, c->from, rb->data, l) == 1) c->tries = 0; if (info->length >= 0) info->length -= l; c->from += l; kill_buffer_data(rb, l); if (!info->length && !rb->close) { setcstate(c, S_OK); http_end_request(c, 0); return; } } else { next_chunk: if (info->chunk_remaining == -2) { int l; if ((l = is_line_in_buffer(rb))) { if (l == -1) { setcstate(c, S_HTTP_ERROR); abort_connection(c); return; } kill_buffer_data(rb, l); if (l <= 2) { setcstate(c, S_OK); http_end_request(c, 0); return; } goto next_chunk; } } else if (info->chunk_remaining == -1) { int l; if ((l = is_line_in_buffer(rb))) { unsigned char *de; long n = 0; /* warning, go away */ if (l != -1) n = strtol(rb->data, (char **)(void *)&de, 16); if (l == -1 || n < 0 || n >= MAXINT || de == rb->data) { setcstate(c, S_HTTP_ERROR); abort_connection(c); return; } kill_buffer_data(rb, l); if (!(info->chunk_remaining = n)) info->chunk_remaining = -2; goto next_chunk; } } else { int l = info->chunk_remaining; if (l > rb->len) l = rb->len; if (c->from + l < 0) { setcstate(c, S_LARGE_FILE); abort_connection(c); return; } c->received += l; if (add_fragment(c->cache, c->from, rb->data, l) == 1) c->tries = 0; info->chunk_remaining -= l; c->from += l; kill_buffer_data(rb, l); if (!info->chunk_remaining && rb->len >= 1) { if (rb->data[0] == 10) kill_buffer_data(rb, 1); else { if (rb->data[0] != 13 || (rb->len >= 2 && rb->data[1] != 10)) { setcstate(c, S_HTTP_ERROR); abort_connection(c); return; } if (rb->len < 2) goto read_more; kill_buffer_data(rb, 2); } info->chunk_remaining = -1; goto next_chunk; } } } read_more: read_from_socket(c, c->sock1, rb, read_http_data); setcstate(c, S_TRANS); }