void server_read(struct bufferevent *bufev, void *arg) { struct session *s = arg; size_t buf_avail, read; int n; bufferevent_settimeout(bufev, timeout, 0); do { buf_avail = sizeof s->sbuf - s->sbuf_valid; read = bufferevent_read(bufev, s->sbuf + s->sbuf_valid, buf_avail); s->sbuf_valid += read; while ((n = get_line(s->sbuf, &s->sbuf_valid)) > 0) { logmsg(LOG_DEBUG, "#%d server: %s", s->id, linebuf); if (!server_parse(s)) { end_session(s); return; } bufferevent_write(s->client_bufev, linebuf, linelen); } if (n == -1) { logmsg(LOG_ERR, "#%d server reply too long or not" " clean", s->id); end_session(s); return; } } while (read == buf_avail); }
static int buffer_event_set_timeouts(lua_State* L) { int timeout_read, timeout_write; le_bufferevent* ev = buffer_event_get(L, 1); if(!ev->ev) return 0; timeout_read = lua_tointeger(L, 2); timeout_write = lua_tointeger(L, 3); bufferevent_settimeout(ev->ev, timeout_read, timeout_write); return 0; }
void client_start_stream(Client *cl) { struct bufferevent *bev = cl->client.bufev; cl->authenticated = true; bufferevent_disable(bev, EV_READ | EV_WRITE); bufferevent_settimeout(bev, SOCKS_IO_TIMEOUT, SOCKS_IO_TIMEOUT); bufferevent_setwatermark(bev, EV_READ, 0, SOCKS_STREAM_BUFSIZ); bufferevent_setcb(bev, on_client_read_stream, on_client_write, on_client_event, cl); bufferevent_enable(bev, EV_READ | EV_WRITE); /* there is still data available in the buffer, call next callback */ if (EVBUFFER_LENGTH(EVBUFFER_INPUT(bev))) on_client_read_stream(bev, cl); }
int main(int argc, char **argv) { event_init(); struct sockaddr_in sa; struct bufferevent *bev; int fd; unsigned ip; inet_aton("213.248.62.7",&ip); sa.sin_addr = *((struct in_addr *) &ip); sa.sin_family = AF_INET; sa.sin_port = htons(22); bzero(&sa.sin_zero, 8); fd=socket(AF_INET, SOCK_STREAM, 0); setnb(fd); connect(fd, (struct sockaddr *) & sa, sizeof (struct sockaddr)); bev = bufferevent_new(fd, OnBufferedRead, OnBufferedWrite, OnBufferedError, NULL); bufferevent_enable(bev, EV_READ); bufferevent_settimeout(bev,10,10); event_dispatch(); return 0; }
void ConnectTo(unsigned ip,unsigned port) { struct Poll *poll=malloc(sizeof(*poll)); struct sockaddr_in sa; poll->IP=ip; poll->port=port; sa.sin_addr = *((struct in_addr *) &ip); // fprintf(stderr,cGREEN" CONNECT TO %s:%d\n"cEND,ipString(ip),poll->port); sa.sin_family = AF_INET; sa.sin_port = htons(port); bzero(&sa.sin_zero, 8); poll->fd=socket(AF_INET, SOCK_STREAM, 0); setnb(poll->fd); connect(poll->fd, (struct sockaddr *) & sa, sizeof (struct sockaddr)); poll->bev = bufferevent_new(poll->fd, OnBufferedRead, OnBufferedWrite, OnBufferedError, poll); bufferevent_enable(poll->bev, EV_WRITE); bufferevent_settimeout(poll->bev,10,10); counter++; }
Client *client_new(SocksLink *sl, int fd, struct sockaddr_storage *addr, socklen_t addrlen) { Client *cl = calloc(sizeof (*cl), 1); struct bufferevent *bev; INIT_LIST_HEAD(&cl->next); INIT_LIST_HEAD(&cl->next_auth); if (!cl) return NULL; bev = bufferevent_new(fd, NULL, NULL, NULL, NULL); if (!bev) { free(cl); return NULL; } bufferevent_base_set(sl->base, bev); cl->client_method = AUTH_METHOD_INVALID; cl->server_method = AUTH_METHOD_INVALID; cl->parent = sl; cl->client.bufev = bev; cl->client.fd = fd; cl->client.addr = *addr; cl->client.addrlen = addrlen; cl->server.fd = -1; list_add(&cl->next, &sl->clients); if (sl->pipe) { client_connect_server(cl); } else { bufferevent_setcb(bev, on_client_read_init, on_client_write, on_client_event, cl); bufferevent_settimeout(bev, SOCKS5_AUTH_TIMEOUT, SOCKS5_AUTH_TIMEOUT); bufferevent_enable(bev, EV_READ|EV_WRITE); } return cl; }
int server_fcgi(struct httpd *env, struct client *clt) { struct server_fcgi_param param; struct server_config *srv_conf = clt->clt_srv_conf; struct http_descriptor *desc = clt->clt_descreq; struct fcgi_record_header *h; struct fcgi_begin_request_body *begin; char hbuf[HOST_NAME_MAX+1]; size_t scriptlen; int pathlen; int fd = -1, ret; const char *stripped, *p, *alias, *errstr = NULL; char *str, *script = NULL; if (srv_conf->socket[0] == ':') { struct sockaddr_storage ss; in_port_t port; p = srv_conf->socket + 1; port = strtonum(p, 0, 0xffff, &errstr); if (errstr != NULL) { log_warn("%s: strtonum %s, %s", __func__, p, errstr); goto fail; } memset(&ss, 0, sizeof(ss)); ss.ss_family = AF_INET; ((struct sockaddr_in *) &ss)->sin_addr.s_addr = htonl(INADDR_LOOPBACK); port = htons(port); if ((fd = server_socket_connect(&ss, port, srv_conf)) == -1) goto fail; } else { struct sockaddr_un sun; size_t len; if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) goto fail; memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_UNIX; len = strlcpy(sun.sun_path, srv_conf->socket, sizeof(sun.sun_path)); if (len >= sizeof(sun.sun_path)) { errstr = "socket path to long"; goto fail; } sun.sun_len = len; if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) goto fail; } socket_set_blockmode(fd, BM_NONBLOCK); memset(hbuf, 0, sizeof(hbuf)); clt->clt_fcgi_state = FCGI_READ_HEADER; clt->clt_fcgi_toread = sizeof(struct fcgi_record_header); if (clt->clt_srvevb != NULL) evbuffer_free(clt->clt_srvevb); clt->clt_srvevb = evbuffer_new(); if (clt->clt_srvevb == NULL) { errstr = "failed to allocate evbuffer"; goto fail; } close(clt->clt_fd); clt->clt_fd = fd; if (clt->clt_srvbev != NULL) bufferevent_free(clt->clt_srvbev); clt->clt_srvbev_throttled = 0; clt->clt_srvbev = bufferevent_new(fd, server_fcgi_read, NULL, server_file_error, clt); if (clt->clt_srvbev == NULL) { errstr = "failed to allocate fcgi buffer event"; goto fail; } memset(¶m, 0, sizeof(param)); h = (struct fcgi_record_header *)¶m.buf; h->version = 1; h->type = FCGI_BEGIN_REQUEST; h->id = htons(1); h->content_len = htons(sizeof(struct fcgi_begin_request_body)); h->padding_len = 0; begin = (struct fcgi_begin_request_body *)¶m.buf[sizeof(struct fcgi_record_header)]; begin->role = htons(FCGI_RESPONDER); if (bufferevent_write(clt->clt_srvbev, ¶m.buf, sizeof(struct fcgi_record_header) + sizeof(struct fcgi_begin_request_body)) == -1) { errstr = "failed to write to evbuffer"; goto fail; } h->type = FCGI_PARAMS; h->content_len = param.total_len = 0; alias = desc->http_path_alias != NULL ? desc->http_path_alias : desc->http_path; stripped = server_root_strip(alias, srv_conf->strip); if ((pathlen = asprintf(&script, "%s%s", srv_conf->root, stripped)) == -1) { errstr = "failed to get script name"; goto fail; } scriptlen = path_info(script); /* * no part of root should show up in PATH_INFO. * therefore scriptlen should be >= strlen(root) */ if (scriptlen < strlen(srv_conf->root)) scriptlen = strlen(srv_conf->root); if ((int)scriptlen < pathlen) { if (fcgi_add_param(¶m, "PATH_INFO", script + scriptlen, clt) == -1) { errstr = "failed to encode param"; goto fail; } script[scriptlen] = '\0'; } else { /* RFC 3875 mandates that PATH_INFO is empty if not set */ if (fcgi_add_param(¶m, "PATH_INFO", "", clt) == -1) { errstr = "failed to encode param"; goto fail; } } /* * calculate length of http SCRIPT_NAME: * add length of stripped prefix, * subtract length of prepended local root */ scriptlen += (stripped - alias) - strlen(srv_conf->root); if ((str = strndup(alias, scriptlen)) == NULL) goto fail; ret = fcgi_add_param(¶m, "SCRIPT_NAME", str, clt); free(str); if (ret == -1) { errstr = "failed to encode param"; goto fail; } if (fcgi_add_param(¶m, "SCRIPT_FILENAME", script, clt) == -1) { errstr = "failed to encode param"; goto fail; } if (desc->http_query) if (fcgi_add_param(¶m, "QUERY_STRING", desc->http_query, clt) == -1) { errstr = "failed to encode param"; goto fail; } if (fcgi_add_param(¶m, "DOCUMENT_ROOT", srv_conf->root, clt) == -1) { errstr = "failed to encode param"; goto fail; } if (fcgi_add_param(¶m, "DOCUMENT_URI", alias, clt) == -1) { errstr = "failed to encode param"; goto fail; } if (fcgi_add_param(¶m, "GATEWAY_INTERFACE", "CGI/1.1", clt) == -1) { errstr = "failed to encode param"; goto fail; } if (srv_conf->flags & SRVFLAG_AUTH) { if (fcgi_add_param(¶m, "REMOTE_USER", clt->clt_remote_user, clt) == -1) { errstr = "failed to encode param"; goto fail; } } /* Add HTTP_* headers */ if (server_headers(clt, desc, server_fcgi_writeheader, ¶m) == -1) { errstr = "failed to encode param"; goto fail; } if (srv_conf->flags & SRVFLAG_TLS) if (fcgi_add_param(¶m, "HTTPS", "on", clt) == -1) { errstr = "failed to encode param"; goto fail; } (void)print_host(&clt->clt_ss, hbuf, sizeof(hbuf)); if (fcgi_add_param(¶m, "REMOTE_ADDR", hbuf, clt) == -1) { errstr = "failed to encode param"; goto fail; } (void)snprintf(hbuf, sizeof(hbuf), "%d", ntohs(clt->clt_port)); if (fcgi_add_param(¶m, "REMOTE_PORT", hbuf, clt) == -1) { errstr = "failed to encode param"; goto fail; } if (fcgi_add_param(¶m, "REQUEST_METHOD", server_httpmethod_byid(desc->http_method), clt) == -1) { errstr = "failed to encode param"; goto fail; } if (!desc->http_query) { if (fcgi_add_param(¶m, "REQUEST_URI", desc->http_path, clt) == -1) { errstr = "failed to encode param"; goto fail; } } else { if (asprintf(&str, "%s?%s", desc->http_path, desc->http_query) == -1) { errstr = "failed to encode param"; goto fail; } ret = fcgi_add_param(¶m, "REQUEST_URI", str, clt); free(str); if (ret == -1) { errstr = "failed to encode param"; goto fail; } } (void)print_host(&clt->clt_srv_ss, hbuf, sizeof(hbuf)); if (fcgi_add_param(¶m, "SERVER_ADDR", hbuf, clt) == -1) { errstr = "failed to encode param"; goto fail; } (void)snprintf(hbuf, sizeof(hbuf), "%d", ntohs(server_socket_getport(&clt->clt_srv_ss))); if (fcgi_add_param(¶m, "SERVER_PORT", hbuf, clt) == -1) { errstr = "failed to encode param"; goto fail; } if (fcgi_add_param(¶m, "SERVER_NAME", srv_conf->name, clt) == -1) { errstr = "failed to encode param"; goto fail; } if (fcgi_add_param(¶m, "SERVER_PROTOCOL", desc->http_version, clt) == -1) { errstr = "failed to encode param"; goto fail; } if (fcgi_add_param(¶m, "SERVER_SOFTWARE", HTTPD_SERVERNAME, clt) == -1) { errstr = "failed to encode param"; goto fail; } if (param.total_len != 0) { /* send last params record */ if (bufferevent_write(clt->clt_srvbev, ¶m.buf, sizeof(struct fcgi_record_header) + ntohs(h->content_len)) == -1) { errstr = "failed to write to client evbuffer"; goto fail; } } /* send "no more params" message */ h->content_len = 0; if (bufferevent_write(clt->clt_srvbev, ¶m.buf, sizeof(struct fcgi_record_header)) == -1) { errstr = "failed to write to client evbuffer"; goto fail; } bufferevent_settimeout(clt->clt_srvbev, srv_conf->timeout.tv_sec, srv_conf->timeout.tv_sec); bufferevent_enable(clt->clt_srvbev, EV_READ|EV_WRITE); if (clt->clt_toread != 0) { server_read_httpcontent(clt->clt_bev, clt); bufferevent_enable(clt->clt_bev, EV_READ); } else { bufferevent_disable(clt->clt_bev, EV_READ); fcgi_add_stdin(clt, NULL); } if (strcmp(desc->http_version, "HTTP/1.1") == 0) { clt->clt_fcgi_chunked = 1; } else { /* HTTP/1.0 does not support chunked encoding */ clt->clt_fcgi_chunked = 0; clt->clt_persist = 0; } clt->clt_fcgi_end = 0; clt->clt_done = 0; free(script); return (0); fail: free(script); if (errstr == NULL) errstr = strerror(errno); server_abort_http(clt, 500, errstr); return (-1); }
int server_file_request(struct httpd *env, struct client *clt, char *path, struct stat *st) { struct server_config *srv_conf = clt->clt_srv_conf; struct media_type *media; const char *errstr = NULL; int fd = -1, ret, code = 500; if ((ret = server_file_method(clt)) != 0) { code = ret; goto abort; } if ((ret = server_file_modified_since(clt->clt_descreq, st)) != -1) return ret; /* Now open the file, should be readable or we have another problem */ if ((fd = open(path, O_RDONLY)) == -1) goto abort; media = media_find(env->sc_mediatypes, path); ret = server_response_http(clt, 200, media, st->st_size, MINIMUM(time(NULL), st->st_mtim.tv_sec)); switch (ret) { case -1: goto fail; case 0: /* Connection is already finished */ close(fd); goto done; default: break; } clt->clt_fd = fd; if (clt->clt_srvbev != NULL) bufferevent_free(clt->clt_srvbev); clt->clt_srvbev = bufferevent_new(clt->clt_fd, server_read, server_write, server_file_error, clt); if (clt->clt_srvbev == NULL) { errstr = "failed to allocate file buffer event"; goto fail; } /* Adjust read watermark to the socket output buffer size */ bufferevent_setwatermark(clt->clt_srvbev, EV_READ, 0, clt->clt_sndbufsiz); bufferevent_settimeout(clt->clt_srvbev, srv_conf->timeout.tv_sec, srv_conf->timeout.tv_sec); bufferevent_enable(clt->clt_srvbev, EV_READ); bufferevent_disable(clt->clt_bev, EV_READ); done: server_reset_http(clt); return (0); fail: bufferevent_disable(clt->clt_bev, EV_READ|EV_WRITE); bufferevent_free(clt->clt_bev); clt->clt_bev = NULL; abort: if (errstr == NULL) errstr = strerror(errno); server_abort_http(clt, code, errstr); return (-1); }
/** * This function will be called by libevent when there is a connection * ready to be accepted. */ void on_accept(int fd, short ev, void *arg) { int client_fd; struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); workqueue_t *workqueue = (workqueue_t *)arg; client_t *client; job_t *job; client_fd = accept(fd, (struct sockaddr *)&client_addr, &client_len); if (client_fd < 0) { warn("accept failed"); return; } printf("Run accept fd:%d\n", fd); /* Set the client socket to non-blocking mode. */ if (setnonblock(client_fd) < 0) { ; warn("failed to set client socket to non-blocking"); close(client_fd); return; } /* Create a client object. */ if ((client = (client_t *)malloc(sizeof(*client))) == NULL) { warn("failed to allocate memory for client state"); close(client_fd); return; } memset(client, 0, sizeof(*client)); client->fd = client_fd; /* Add any custom code anywhere from here to the end of this function * to initialize your application-specific attributes in the client struct. */ if ((client->output_buffer = evbuffer_new()) == NULL) { warn("client output buffer allocation failed"); closeAndFreeClient(client); return; } if ((client->evbase = event_base_new()) == NULL) { warn("client event_base creation failed"); closeAndFreeClient(client); return; } /* Create the buffered event. * * The first argument is the file descriptor that will trigger * the events, in this case the clients socket. * * The second argument is the callback that will be called * when data has been read from the socket and is available to * the application. * * The third argument is a callback to a function that will be * called when the write buffer has reached a low watermark. * That usually means that when the write buffer is 0 length, * this callback will be called. It must be defined, but you * don't actually have to do anything in this callback. * * The fourth argument is a callback that will be called when * there is a socket error. This is where you will detect * that the client disconnected or other socket errors. * * The fifth and final argument is to store an argument in * that will be passed to the callbacks. We store the client * object here. */ if ((client->buf_ev = bufferevent_new(client_fd, buffered_on_read, buffered_on_write, buffered_on_error, client)) == NULL) { warn("client bufferevent creation failed"); closeAndFreeClient(client); return; } bufferevent_base_set(client->evbase, client->buf_ev); bufferevent_settimeout(client->buf_ev, SOCKET_READ_TIMEOUT_SECONDS, SOCKET_WRITE_TIMEOUT_SECONDS); /* We have to enable it before our callbacks will be * called. */ bufferevent_enable(client->buf_ev, EV_READ); /* Create a job object and add it to the work queue. */ if ((job = (job_t *)malloc(sizeof(*job))) == NULL) { warn("failed to allocate memory for job state"); closeAndFreeClient(client); return; } job->job_function = server_job_function; job->user_data = client; workqueue_add_job(workqueue, job); }
void handle_connection(const int listen_fd, short event, void *arg) { struct sockaddr_storage tmp_ss; struct sockaddr *client_sa, *server_sa, *fixed_server_sa; struct sockaddr *proxy_to_server_sa; struct session *s; socklen_t len; int client_fd, fc, on; event_add(&listen_ev, NULL); if ((event & EV_TIMEOUT)) /* accept() is no longer paused. */ return; /* * We _must_ accept the connection, otherwise libevent will keep * coming back, and we will chew up all CPU. */ client_sa = sstosa(&tmp_ss); len = sizeof(struct sockaddr_storage); if ((client_fd = accept(listen_fd, client_sa, &len)) < 0) { logmsg(LOG_CRIT, "accept() failed: %s", strerror(errno)); /* * Pause accept if we are out of file descriptors, or * libevent will haunt us here too. */ if (errno == ENFILE || errno == EMFILE) { struct timeval pause = { 1, 0 }; event_del(&listen_ev); evtimer_add(&pause_accept_ev, &pause); } else if (errno != EWOULDBLOCK && errno != EINTR && errno != ECONNABORTED) logmsg(LOG_CRIT, "accept() failed: %s", strerror(errno)); return; } /* Refuse connection if the maximum is reached. */ if (session_count >= max_sessions) { logmsg(LOG_ERR, "client limit (%d) reached, refusing " "connection from %s", max_sessions, sock_ntop(client_sa)); close(client_fd); return; } /* Allocate session and copy back the info from the accept(). */ s = init_session(); if (s == NULL) { logmsg(LOG_CRIT, "init_session failed"); close(client_fd); return; } s->client_fd = client_fd; memcpy(sstosa(&s->client_ss), client_sa, client_sa->sa_len); /* Cast it once, and be done with it. */ client_sa = sstosa(&s->client_ss); server_sa = sstosa(&s->server_ss); proxy_to_server_sa = sstosa(&s->proxy_ss); fixed_server_sa = sstosa(&fixed_server_ss); /* Log id/client early to ease debugging. */ logmsg(LOG_DEBUG, "#%d accepted connection from %s", s->id, sock_ntop(client_sa)); /* * Find out the real server and port that the client wanted. */ len = sizeof(struct sockaddr_storage); if (getsockname(s->client_fd, server_sa, &len) < 0) { logmsg(LOG_CRIT, "#%d getsockname failed: %s", s->id, strerror(errno)); goto fail; } len = sizeof(s->client_rd); if (getsockopt(s->client_fd, SOL_SOCKET, SO_RTABLE, &s->client_rd, &len) && errno != ENOPROTOOPT) { logmsg(LOG_CRIT, "#%d getsockopt failed: %s", s->id, strerror(errno)); goto fail; } if (fixed_server) { memcpy(sstosa(&s->orig_server_ss), server_sa, server_sa->sa_len); memcpy(server_sa, fixed_server_sa, fixed_server_sa->sa_len); } /* XXX: check we are not connecting to ourself. */ /* * Setup socket and connect to server. */ if ((s->server_fd = socket(server_sa->sa_family, SOCK_STREAM, IPPROTO_TCP)) < 0) { logmsg(LOG_CRIT, "#%d server socket failed: %s", s->id, strerror(errno)); goto fail; } if (fixed_proxy && bind(s->server_fd, sstosa(&fixed_proxy_ss), fixed_proxy_ss.ss_len) != 0) { logmsg(LOG_CRIT, "#%d cannot bind fixed proxy address: %s", s->id, strerror(errno)); goto fail; } /* Use non-blocking connect(), see CONNECT_TIMEOUT below. */ if ((fc = fcntl(s->server_fd, F_GETFL)) == -1 || fcntl(s->server_fd, F_SETFL, fc | O_NONBLOCK) == -1) { logmsg(LOG_CRIT, "#%d cannot mark socket non-blocking: %s", s->id, strerror(errno)); goto fail; } if (connect(s->server_fd, server_sa, server_sa->sa_len) < 0 && errno != EINPROGRESS) { logmsg(LOG_CRIT, "#%d proxy cannot connect to server %s: %s", s->id, sock_ntop(server_sa), strerror(errno)); goto fail; } len = sizeof(struct sockaddr_storage); if ((getsockname(s->server_fd, proxy_to_server_sa, &len)) < 0) { logmsg(LOG_CRIT, "#%d getsockname failed: %s", s->id, strerror(errno)); goto fail; } logmsg(LOG_INFO, "#%d FTP session %d/%d started: client %s to server " "%s via proxy %s", s->id, session_count, max_sessions, sock_ntop(client_sa), sock_ntop(server_sa), sock_ntop(proxy_to_server_sa)); /* Keepalive is nice, but don't care if it fails. */ on = 1; setsockopt(s->client_fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof on); setsockopt(s->server_fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof on); /* * Setup buffered events. */ s->client_bufev = bufferevent_new(s->client_fd, &client_read, NULL, &client_error, s); if (s->client_bufev == NULL) { logmsg(LOG_CRIT, "#%d bufferevent_new client failed", s->id); goto fail; } bufferevent_settimeout(s->client_bufev, timeout, 0); bufferevent_enable(s->client_bufev, EV_READ | EV_TIMEOUT); s->server_bufev = bufferevent_new(s->server_fd, &server_read, NULL, &server_error, s); if (s->server_bufev == NULL) { logmsg(LOG_CRIT, "#%d bufferevent_new server failed", s->id); goto fail; } bufferevent_settimeout(s->server_bufev, CONNECT_TIMEOUT, 0); bufferevent_enable(s->server_bufev, EV_READ | EV_TIMEOUT); return; fail: end_session(s); }
int server_partial_file_request(struct httpd *env, struct client *clt, char *path, struct stat *st, char *range_str) { struct server_config *srv_conf = clt->clt_srv_conf; struct http_descriptor *resp = clt->clt_descresp; struct http_descriptor *desc = clt->clt_descreq; struct media_type *media, multipart_media; struct range_data *r = &clt->clt_ranges; struct range *range; size_t content_length = 0; int code = 500, fd = -1, i, nranges, ret; char content_range[64]; const char *errstr = NULL; /* Ignore range request for methods other than GET */ if (desc->http_method != HTTP_METHOD_GET) return server_file_request(env, clt, path, st); if ((nranges = parse_ranges(clt, range_str, st->st_size)) < 1) { code = 416; (void)snprintf(content_range, sizeof(content_range), "bytes */%lld", st->st_size); errstr = content_range; goto abort; } /* Now open the file, should be readable or we have another problem */ if ((fd = open(path, O_RDONLY)) == -1) goto abort; media = media_find_config(env, srv_conf, path); r->range_media = media; if (nranges == 1) { range = &r->range[0]; (void)snprintf(content_range, sizeof(content_range), "bytes %lld-%lld/%lld", range->start, range->end, st->st_size); if (kv_add(&resp->http_headers, "Content-Range", content_range) == NULL) goto abort; range = &r->range[0]; content_length += range->end - range->start + 1; } else { /* Add boundary, all parts will be handled by the callback */ arc4random_buf(&clt->clt_boundary, sizeof(clt->clt_boundary)); /* Calculate Content-Length of the complete multipart body */ for (i = 0; i < nranges; i++) { range = &r->range[i]; /* calculate Content-Length of the complete body */ if ((ret = snprintf(NULL, 0, "\r\n--%llu\r\n" "Content-Type: %s/%s\r\n" "Content-Range: bytes %lld-%lld/%lld\r\n\r\n", clt->clt_boundary, media->media_type, media->media_subtype, range->start, range->end, st->st_size)) < 0) goto abort; /* Add data length */ content_length += ret + range->end - range->start + 1; } if ((ret = snprintf(NULL, 0, "\r\n--%llu--\r\n", clt->clt_boundary)) < 0) goto abort; content_length += ret; /* prepare multipart/byteranges media type */ (void)strlcpy(multipart_media.media_type, "multipart", sizeof(multipart_media.media_type)); (void)snprintf(multipart_media.media_subtype, sizeof(multipart_media.media_subtype), "byteranges; boundary=%llu", clt->clt_boundary); media = &multipart_media; } /* Start with first range */ r->range_toread = TOREAD_HTTP_RANGE; ret = server_response_http(clt, 206, media, content_length, MINIMUM(time(NULL), st->st_mtim.tv_sec)); switch (ret) { case -1: goto fail; case 0: /* Connection is already finished */ close(fd); goto done; default: break; } clt->clt_fd = fd; if (clt->clt_srvbev != NULL) bufferevent_free(clt->clt_srvbev); clt->clt_srvbev_throttled = 0; clt->clt_srvbev = bufferevent_new(clt->clt_fd, server_read_httprange, server_write, server_file_error, clt); if (clt->clt_srvbev == NULL) { errstr = "failed to allocate file buffer event"; goto fail; } /* Adjust read watermark to the socket output buffer size */ bufferevent_setwatermark(clt->clt_srvbev, EV_READ, 0, clt->clt_sndbufsiz); bufferevent_settimeout(clt->clt_srvbev, srv_conf->timeout.tv_sec, srv_conf->timeout.tv_sec); bufferevent_enable(clt->clt_srvbev, EV_READ); bufferevent_disable(clt->clt_bev, EV_READ); done: server_reset_http(clt); return (0); fail: bufferevent_disable(clt->clt_bev, EV_READ|EV_WRITE); bufferevent_free(clt->clt_bev); clt->clt_bev = NULL; abort: if (fd != -1) close(fd); if (errstr == NULL) errstr = strerror(errno); server_abort_http(clt, code, errstr); return (-1); }