Beispiel #1
0
int
server_partial_file_request(struct httpd *env, struct client *clt, char *path,
                            struct stat *st, char *range_str)
{
    struct http_descriptor	*resp = clt->clt_descresp;
    struct http_descriptor	*desc = clt->clt_descreq;
    struct media_type	*media, multipart_media;
    struct range		*range;
    struct evbuffer		*evb = NULL;
    size_t			 content_length;
    int			 code = 500, fd = -1, i, nranges, ret;
    uint32_t		 boundary;
    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 ((range = parse_range(range_str, st->st_size, &nranges)) == NULL) {
        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(env->sc_mediatypes, path);
    if ((evb = evbuffer_new()) == NULL) {
        errstr = "failed to allocate file buffer";
        goto abort;
    }

    if (nranges == 1) {
        (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;

        content_length = range->end - range->start + 1;
        if (buffer_add_range(fd, evb, range) == 0)
            goto abort;

    } else {
        content_length = 0;
        boundary = arc4random();
        /* Generate a multipart payload of byteranges */
        while (nranges--) {
            if ((i = evbuffer_add_printf(evb, "\r\n--%ud\r\n",
                                         boundary)) == -1)
                goto abort;

            content_length += i;
            if ((i = evbuffer_add_printf(evb,
                                         "Content-Type: %s/%s\r\n",
                                         media == NULL ? "application" : media->media_type,
                                         media == NULL ?
                                         "octet-stream" : media->media_subtype)) == -1)
                goto abort;

            content_length += i;
            if ((i = evbuffer_add_printf(evb,
                                         "Content-Range: bytes %lld-%lld/%lld\r\n\r\n",
                                         range->start, range->end, st->st_size)) == -1)
                goto abort;

            content_length +=i;
            if (buffer_add_range(fd, evb, range) == 0)
                goto abort;

            content_length += range->end - range->start + 1;
            range++;
        }

        if ((i = evbuffer_add_printf(evb, "\r\n--%ud--\r\n",
                                     boundary)) == -1)
            goto abort;

        content_length += i;

        /* 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=%ud", boundary);
        media = &multipart_media;
    }

    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);
        evbuffer_free(evb);
        evb = NULL;
        goto done;
    default:
        break;
    }

    if (server_bufferevent_write_buffer(clt, evb) == -1)
        goto fail;

    evbuffer_free(evb);
    evb = NULL;

    bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE);
    if (clt->clt_persist)
        clt->clt_toread = TOREAD_HTTP_HEADER;
    else
        clt->clt_toread = TOREAD_HTTP_NONE;
    clt->clt_done = 0;

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);
}
Beispiel #2
0
int
server_file_index(struct httpd *env, struct client *clt, struct stat *st)
{
    char			  path[PATH_MAX];
    char			  tmstr[21];
    struct http_descriptor	 *desc = clt->clt_descreq;
    struct server_config	 *srv_conf = clt->clt_srv_conf;
    struct dirent		**namelist, *dp;
    int			  namesize, i, ret, fd = -1, namewidth, skip;
    int			  code = 500;
    struct evbuffer		 *evb = NULL;
    struct media_type	 *media;
    const char		 *stripped, *style;
    char			 *escapeduri, *escapedhtml, *escapedpath;
    struct tm		  tm;
    time_t			  t, dir_mtime;

    if ((ret = server_file_method(clt)) != 0) {
        code = ret;
        goto abort;
    }

    /* Request path is already canonicalized */
    stripped = server_root_strip(desc->http_path, srv_conf->strip);
    if ((size_t)snprintf(path, sizeof(path), "%s%s",
                         srv_conf->root, stripped) >= sizeof(path))
        goto abort;

    /* Now open the file, should be readable or we have another problem */
    if ((fd = open(path, O_RDONLY)) == -1)
        goto abort;

    /* Save last modification time */
#if __FreeBSD_version < 900011
    dir_mtime = MINIMUM(time(NULL), st->st_mtimespec.tv_sec);
#else
    dir_mtime = MINIMUM(time(NULL), st->st_mtim.tv_sec);
#endif

    if ((evb = evbuffer_new()) == NULL)
        goto abort;

    if ((namesize = scandir(path, &namelist, NULL, alphasort)) == -1)
        goto abort;

    /* Indicate failure but continue going through the list */
    skip = 0;

    if ((escapedpath = escape_html(desc->http_path)) == NULL)
        goto fail;

    /* A CSS stylesheet allows minimal customization by the user */
    style = "body { background-color: white; color: black; font-family: "
            "sans-serif; }\nhr { border: 0; border-bottom: 1px dashed; }\n";
    /* Generate simple HTML index document */
    if (evbuffer_add_printf(evb,
                            "<!DOCTYPE html>\n"
                            "<html>\n"
                            "<head>\n"
                            "<title>Index of %s</title>\n"
                            "<style type=\"text/css\"><!--\n%s\n--></style>\n"
                            "</head>\n"
                            "<body>\n"
                            "<h1>Index of %s</h1>\n"
                            "<hr>\n<pre>\n",
                            escapedpath, style, escapedpath) == -1)
        skip = 1;

    free(escapedpath);

    for (i = 0; i < namesize; i++) {
        dp = namelist[i];

        if (skip ||
                fstatat(fd, dp->d_name, st, 0) == -1) {
            free(dp);
            continue;
        }

        t = st->st_mtime;
        localtime_r(&t, &tm);
        strftime(tmstr, sizeof(tmstr), "%d-%h-%Y %R", &tm);
        namewidth = 51 - strlen(dp->d_name);

        if ((escapeduri = url_encode(dp->d_name)) == NULL)
            goto fail;
        if ((escapedhtml = escape_html(dp->d_name)) == NULL)
            goto fail;

        if (dp->d_name[0] == '.' &&
                !(dp->d_name[1] == '.' && dp->d_name[2] == '\0')) {
            /* ignore hidden files starting with a dot */
        } else if (S_ISDIR(st->st_mode)) {
            namewidth -= 1; /* trailing slash */
            if (evbuffer_add_printf(evb,
                                    "<a href=\"%s%s/\">%s/</a>%*s%s%20s\n",
                                    strchr(escapeduri, ':') != NULL ? "./" : "",
                                    escapeduri, escapedhtml,
                                    MAXIMUM(namewidth, 0), " ", tmstr, "-") == -1)
                skip = 1;
        } else if (S_ISREG(st->st_mode)) {
            if (evbuffer_add_printf(evb,
                                    "<a href=\"%s%s\">%s</a>%*s%s%20llu\n",
                                    strchr(escapeduri, ':') != NULL ? "./" : "",
                                    escapeduri, escapedhtml,
                                    MAXIMUM(namewidth, 0), " ",
                                    tmstr, st->st_size) == -1)
                skip = 1;
        }
        free(escapeduri);
        free(escapedhtml);
        free(dp);
    }
    free(namelist);

    if (skip ||
            evbuffer_add_printf(evb,
                                "</pre>\n<hr>\n</body>\n</html>\n") == -1)
        goto abort;

    close(fd);
    fd = -1;

    media = media_find(env->sc_mediatypes, "index.html");
    ret = server_response_http(clt, 200, media, EVBUFFER_LENGTH(evb),
                               dir_mtime);
    switch (ret) {
    case -1:
        goto fail;
    case 0:
        /* Connection is already finished */
        evbuffer_free(evb);
        goto done;
    default:
        break;
    }

    if (server_bufferevent_write_buffer(clt, evb) == -1)
        goto fail;
    evbuffer_free(evb);
    evb = NULL;

    bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE);
    if (clt->clt_persist)
        clt->clt_toread = TOREAD_HTTP_HEADER;
    else
        clt->clt_toread = TOREAD_HTTP_NONE;
    clt->clt_done = 0;

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 (evb != NULL)
        evbuffer_free(evb);
    server_abort_http(clt, code, desc->http_path);
    return (-1);
}
Beispiel #3
0
void httpc_read_cb(struct bufferevent *buffev, void *_arg)
{
	redsocks_client *client = _arg;
	int dropped = 0;

	assert(client->state >= httpc_request_sent);

	redsocks_touch_client(client);

	if (client->state == httpc_request_sent) {
		size_t len = EVBUFFER_LENGTH(buffev->input);
		char *line = redsocks_evbuffer_readline(buffev->input);
		if (line) {
			unsigned int code;
			if (sscanf(line, "HTTP/%*u.%*u %u", &code) == 1) { // 1 == one _assigned_ match
				if (code == 407) { // auth failed
					http_auth *auth = (void*)(client->instance + 1);

					if (auth->last_auth_query != NULL && auth->last_auth_count == 1) {
						redsocks_log_error(client, LOG_NOTICE, "proxy auth failed");
						redsocks_drop_client(client);

						dropped = 1;
					} else if (client->instance->config.login == NULL || client->instance->config.password == NULL) {
						redsocks_log_error(client, LOG_NOTICE, "proxy auth required, but no login information provided");
						redsocks_drop_client(client);

						dropped = 1;
					} else {
						char *auth_request = get_auth_request_header(buffev->input);

						if (!auth_request) {
							redsocks_log_error(client, LOG_NOTICE, "403 found, but no proxy auth challenge");
							redsocks_drop_client(client);
							dropped = 1;
						} else {
							free(line);
							free(auth->last_auth_query);
							char *ptr = auth_request;

							ptr += strlen(auth_request_header);
							while (isspace(*ptr))
								ptr++;

							size_t last_auth_query_len = strlen(ptr) + 1;
							auth->last_auth_query = calloc(last_auth_query_len, 1);
							memcpy(auth->last_auth_query, ptr, last_auth_query_len);
							auth->last_auth_count = 0;

							free(auth_request);

							if (bufferevent_disable(client->relay, EV_WRITE)) {
								redsocks_log_errno(client, LOG_ERR, "bufferevent_disable");
								return;
							}

							/* close relay tunnel */
							redsocks_close(EVENT_FD(&client->relay->ev_write));
							bufferevent_free(client->relay);

							/* set to initial state*/
							client->state = httpc_new;

							/* and reconnect */
							redsocks_connect_relay(client);
							return;
						}
					}
				} else if (200 <= code && code <= 299) {
					client->state = httpc_reply_came;
				} else {
					redsocks_log_error(client, LOG_NOTICE, "%s", line);
					redsocks_drop_client(client);
					dropped = 1;
				}
			}
			free(line);
		}
		else if (len >= HTTP_HEAD_WM_HIGH) {
			redsocks_drop_client(client);
			dropped = 1;
		}
	}

	if (dropped)
		return;

	while (client->state == httpc_reply_came) {
		char *line = redsocks_evbuffer_readline(buffev->input);
		if (line) {
			if (strlen(line) == 0) {
				client->state = httpc_headers_skipped;
			}
			free(line);
		}
		else {
			break;
		}
	}

	if (client->state == httpc_headers_skipped) {
		redsocks_start_relay(client);
	}
}
Beispiel #4
0
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,
#if __FreeBSD_version < 900011
                               MINIMUM(time(NULL), st->st_mtimespec.tv_sec));
#else
                               MINIMUM(time(NULL), st->st_mtim.tv_sec));
#endif
    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);
}
static void
evhttp_send_continue_done(struct evhttp_connection *evcon, void *arg)
{
	bufferevent_disable(evcon->bufev, EV_WRITE);
}
static void
socks_read_cb(struct bufferevent *bev, void *ptr)
{
	obfsproxyssh_client_session_t *session = ptr;
	obfsproxyssh_t *state = session->client->state;
	struct evbuffer *buf;
	struct sockaddr_in addr;
	unsigned char *p, *userid;
	size_t len;
	int i;

	assert(bev == session->socks_ev);

	buf = bufferevent_get_input(bev);
	len = evbuffer_get_length(buf);
	if (len < SOCKS_4_CONNECT_REQUEST_LEN)
		return;

	p = evbuffer_pullup(buf, len);
	if (NULL == p) {
		log_f(state, "SOCKS: Error: %s Failed to pullup (OOM?)",
				bdata(session->socks_addr));
		goto out_free;
	}

	/*
	 * Parse the SOCKS 4 CONNECT
	 *
	 * uint8_4  VN  -> 4
	 * uint8_t  CN	-> 1
	 * uint16_t DSTPORT
	 * uint32_t DSTIP
	 * uint8_t  USERID[] (Tor PT arguments)
	 * uint8_t  NULL -> 0
	 */

	if (SOCKS_4_VER != p[0]) {
		log_f(state, "SOCKS: Error: %s Invalid SOCKS protocol version %d",
				bdata(session->socks_addr),
				p[0]);
		goto out_free;
	}

	if (SOCKS_4_CMD_CONNECT != p[1]) {
		log_f(state, "SOCKS: Error: %s Invalid SOCKS 4 command %d",
				bdata(session->socks_addr),
				p[1]);
		goto out_free;
	}

	userid = p + SOCKS_4_CONNECT_REQUEST_LEN;
	for (i = 0; i < len - SOCKS_4_CONNECT_REQUEST_LEN; i++) {
		if ('\0' == userid[i]) {
			if (len != SOCKS_4_CONNECT_REQUEST_LEN + i + 1) {
				log_f(state, "SOCKS: Error: %s Trailing garbage after CONNECT",
						bdata(session->socks_addr));
				goto out_free;
			}
			bufferevent_disable(bev, EV_READ);

			memset(&addr, 0, sizeof(addr));
			addr.sin_family = AF_INET;
			addr.sin_port = *(uint16_t *) (p + 2);
			addr.sin_addr.s_addr = *(uint32_t *) (p + 4);

			if (ssh_new_connection(session, &addr, (char *) userid))
				goto out_free;

			evbuffer_drain(buf, len);

			return;
		}
	}

	if (len > OBFSPROXYSSH_CLIENT_MAX_SOCKS_REQ) {
		log_f(state, "SOCKS: Error: %s SOCKS 4 Request too big",
				bdata(session->socks_addr));
		goto out_free;
	}
	return;

out_free:
	session_free(session);
	return;
}
Beispiel #7
0
/** Called on successful connections and errors */
void
event_cb(struct bufferevent *bev, short e, void *data)
{
  struct conn *c = data;
  uint32_t error_conditions =
    BEV_EVENT_EOF | BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT;

#if SSL_DEBUG_LEVEL > 1
  errprintf(stdout, "ssl_slave: event callback triggered with flags 0x%hx\n",
            e);
#endif

  if (e & BEV_EVENT_CONNECTED) {
    if (c->local_bev == bev) {
      local_connected(c);
    } else {
      ssl_connected(c);
    }
  } else if (e & BEV_EVENT_TIMEOUT) {
    if (c->state == C_SSL_CONNECTING) {
      /* Handshake timed out. */
#if SSL_DEBUG_LEVEL > 0
      errprintf(stdout, "ssl_slave: SSL handshake timeout.\n");
#endif
      bufferevent_disable(c->remote_bev, EV_READ | EV_WRITE);
      bufferevent_free(c->remote_bev);
      c->remote_bev = NULL;
      c->state = C_SHUTTINGDOWN;
      if (c->local_bev) {
        bufferevent_disable(c->local_bev, EV_READ);
        bufferevent_flush(c->local_bev, EV_WRITE, BEV_FINISHED);
      }
      delete_conn(c);
    } else {
      /* Bug in some versions of libevent cause this to trigger when
         it shouldn't. Ignore. */
      return;
    }
  } else if (e & error_conditions) {
    if (c->local_bev == bev) {
      /* Mush side of the connection went away. Flush SSL buffer and shut down. */
#if SSL_DEBUG_LEVEL > 0
      errprintf(stdout,
                "ssl_slave: Lost local connection. State: %d, reason 0x%hx.\n",
                c->state, e);
#endif
      bufferevent_disable(c->local_bev, EV_READ | EV_WRITE);
      bufferevent_free(c->local_bev);
      c->local_bev = NULL;
      c->state = C_SHUTTINGDOWN;
      if (c->remote_bev) {
        bufferevent_disable(c->remote_bev, EV_READ);
        bufferevent_flush(c->remote_bev, EV_WRITE, BEV_FINISHED);
        SSL_shutdown(bufferevent_openssl_get_ssl(c->remote_bev));
      }
      delete_conn(c);
    } else {
      /* Remote side of the connection went away. Flush mush buffer and shut down. */
#if SSL_DEBUG_LEVEL > 0
      errprintf(stdout,
                "ssl_slave: Lost SSL connection. State: %d, reason 0x%hx.\n",
                c->state, e);
#endif
      bufferevent_disable(c->remote_bev, EV_READ | EV_WRITE);
      bufferevent_free(c->remote_bev);
      c->remote_bev = NULL;
      c->state = C_SHUTTINGDOWN;
      if (c->local_bev) {
        bufferevent_disable(c->local_bev, EV_READ);
        bufferevent_flush(c->local_bev, EV_WRITE, BEV_FINISHED);
      }
      delete_conn(c);
    }
  }
}
Beispiel #8
0
void
test_bufferevent_zlib(void *arg)
{
	struct bufferevent *bev1=NULL, *bev2=NULL;
	char buffer[8333];
	z_stream z_input, z_output;
	int i, r;
	evutil_socket_t pair[2] = {-1, -1};
	(void)arg;

	infilter_calls = outfilter_calls = readcb_finished = writecb_finished
	    = errorcb_invoked = 0;

	if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
		tt_abort_perror("socketpair");
	}

	evutil_make_socket_nonblocking(pair[0]);
	evutil_make_socket_nonblocking(pair[1]);

	bev1 = bufferevent_socket_new(NULL, pair[0], 0);
	bev2 = bufferevent_socket_new(NULL, pair[1], 0);

	memset(&z_output, 0, sizeof(z_output));
	r = deflateInit(&z_output, Z_DEFAULT_COMPRESSION);
	tt_int_op(r, ==, Z_OK);
	memset(&z_input, 0, sizeof(z_input));
	r = inflateInit(&z_input);
	tt_int_op(r, ==, Z_OK);

	/* initialize filters */
	bev1 = bufferevent_filter_new(bev1, NULL, zlib_output_filter,
	    BEV_OPT_CLOSE_ON_FREE, zlib_deflate_free, &z_output);
	bev2 = bufferevent_filter_new(bev2, zlib_input_filter,
	    NULL, BEV_OPT_CLOSE_ON_FREE, zlib_inflate_free, &z_input);
	bufferevent_setcb(bev1, readcb, writecb, errorcb, NULL);
	bufferevent_setcb(bev2, readcb, writecb, errorcb, NULL);

	bufferevent_disable(bev1, EV_READ);
	bufferevent_enable(bev1, EV_WRITE);

	bufferevent_enable(bev2, EV_READ);

	for (i = 0; i < (int)sizeof(buffer); i++)
		buffer[i] = i;

	/* break it up into multiple buffer chains */
	bufferevent_write(bev1, buffer, 1800);
	bufferevent_write(bev1, buffer + 1800, sizeof(buffer) - 1800);

	/* we are done writing - we need to flush everything */
	bufferevent_flush(bev1, EV_WRITE, BEV_FINISHED);

	event_dispatch();

	tt_want(infilter_calls);
	tt_want(outfilter_calls);
	tt_want(readcb_finished);
	tt_want(writecb_finished);
	tt_want(!errorcb_invoked);

	test_ok = 1;
end:
	if (bev1)
		bufferevent_free(bev1);
	if (bev2)
		bufferevent_free(bev2);

	if (pair[0] >= 0)
		evutil_closesocket(pair[0]);
	if (pair[1] >= 0)
		evutil_closesocket(pair[1]);
}
Beispiel #9
0
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 = 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(&param, 0, sizeof(param));

	h = (struct fcgi_record_header *)&param.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 *)&param.buf[sizeof(struct
	    fcgi_record_header)];
	begin->role = htons(FCGI_RESPONDER);

	bufferevent_write(clt->clt_srvbev, &param.buf,
	    sizeof(struct fcgi_record_header) +
	    sizeof(struct fcgi_begin_request_body));

	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(&param, "PATH_INFO",
		    script + scriptlen, clt) == -1) {
			errstr = "failed to encode param";
			goto fail;
		}
		script[scriptlen] = '\0';
	}

	/*
	 * 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(&param, "SCRIPT_NAME", str, clt);
	free(str);
	if (ret == -1) {
		errstr = "failed to encode param";
		goto fail;
	}
	if (fcgi_add_param(&param, "SCRIPT_FILENAME", script, clt) == -1) {
		errstr = "failed to encode param";
		goto fail;
	}

	if (desc->http_query)
		if (fcgi_add_param(&param, "QUERY_STRING", desc->http_query,
		    clt) == -1) {
			errstr = "failed to encode param";
			goto fail;
		}

	if (fcgi_add_param(&param, "DOCUMENT_ROOT", srv_conf->root,
	    clt) == -1) {
		errstr = "failed to encode param";
		goto fail;
	}
	if (fcgi_add_param(&param, "DOCUMENT_URI", alias,
	    clt) == -1) {
		errstr = "failed to encode param";
		goto fail;
	}
	if (fcgi_add_param(&param, "GATEWAY_INTERFACE", "CGI/1.1",
	    clt) == -1) {
		errstr = "failed to encode param";
		goto fail;
	}

	if (srv_conf->flags & SRVFLAG_AUTH) {
		if (fcgi_add_param(&param, "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, &param) == -1) {
		errstr = "failed to encode param";
		goto fail;
	}

	if (srv_conf->flags & SRVFLAG_TLS)
		if (fcgi_add_param(&param, "HTTPS", "on", clt) == -1) {
			errstr = "failed to encode param";
			goto fail;
		}

	(void)print_host(&clt->clt_ss, hbuf, sizeof(hbuf));
	if (fcgi_add_param(&param, "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(&param, "REMOTE_PORT", hbuf, clt) == -1) {
		errstr = "failed to encode param";
		goto fail;
	}

	if (fcgi_add_param(&param, "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(&param, "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) {
		ret = fcgi_add_param(&param, "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(&param, "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(&param, "SERVER_PORT", hbuf, clt) == -1) {
		errstr = "failed to encode param";
		goto fail;
	}

	if (fcgi_add_param(&param, "SERVER_NAME", srv_conf->name,
	    clt) == -1) {
		errstr = "failed to encode param";
		goto fail;
	}

	if (fcgi_add_param(&param, "SERVER_PROTOCOL", desc->http_version,
	    clt) == -1) {
		errstr = "failed to encode param";
		goto fail;
	}

	if (fcgi_add_param(&param, "SERVER_SOFTWARE", HTTPD_SERVERNAME,
	    clt) == -1) {
		errstr = "failed to encode param";
		goto fail;
	}

	if (param.total_len != 0) {	/* send last params record */
		bufferevent_write(clt->clt_srvbev, &param.buf,
		    sizeof(struct fcgi_record_header) +
		    ntohs(h->content_len));
	}

	/* send "no more params" message */
	h->content_len = 0;
	bufferevent_write(clt->clt_srvbev, &param.buf,
	    sizeof(struct fcgi_record_header));

	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);
}
Beispiel #10
0
 int BufferEvent::Disable(short ev_type) {
     return bufferevent_disable(bufev_, ev_type);
 }
Beispiel #11
0
/**
 * Link bufferevent with monitor connection.
 * @param[in] conn Monitor connection.
 * @param[in] bev Bufferevent instance.
 */
void zmonitor_conn_set_listener(struct zmonitor_conn *conn, struct bufferevent *bev)
{
    bufferevent_disable(bev, EV_READ);
    bufferevent_setcb(bev, NULL, NULL, zmonitor_event_cb, conn);
    conn->bev = bev;
}
Beispiel #12
0
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);
}
/** HTTP request callback */
static void http_request_cb(struct evhttp_request* req, void* arg)
{
    // Disable reading to work around a libevent bug, fixed in 2.2.0.
    if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) {
        evhttp_connection* conn = evhttp_request_get_connection(req);
        if (conn) {
            bufferevent* bev = evhttp_connection_get_bufferevent(conn);
            if (bev) {
                bufferevent_disable(bev, EV_READ);
            }
        }
    }
    std::unique_ptr<HTTPRequest> hreq(new HTTPRequest(req));

    // Early address-based allow check
    if (!ClientAllowed(hreq->GetPeer())) {
        LogPrint(BCLog::HTTP, "HTTP request from %s rejected: Client network is not allowed RPC access\n",
                 hreq->GetPeer().ToString());
        hreq->WriteReply(HTTP_FORBIDDEN);
        return;
    }

    // Early reject unknown HTTP methods
    if (hreq->GetRequestMethod() == HTTPRequest::UNKNOWN) {
        LogPrint(BCLog::HTTP, "HTTP request from %s rejected: Unknown HTTP request method\n",
                 hreq->GetPeer().ToString());
        hreq->WriteReply(HTTP_BADMETHOD);
        return;
    }

    LogPrint(BCLog::HTTP, "Received a %s request for %s from %s\n",
             RequestMethodString(hreq->GetRequestMethod()), SanitizeString(hreq->GetURI(), SAFE_CHARS_URI).substr(0, 100), hreq->GetPeer().ToString());

    // Find registered handler for prefix
    std::string strURI = hreq->GetURI();
    std::string path;
    std::vector<HTTPPathHandler>::const_iterator i = pathHandlers.begin();
    std::vector<HTTPPathHandler>::const_iterator iend = pathHandlers.end();
    for (; i != iend; ++i) {
        bool match = false;
        if (i->exactMatch)
            match = (strURI == i->prefix);
        else
            match = (strURI.substr(0, i->prefix.size()) == i->prefix);
        if (match) {
            path = strURI.substr(i->prefix.size());
            break;
        }
    }

    // Dispatch to worker thread
    if (i != iend) {
        std::unique_ptr<HTTPWorkItem> item(new HTTPWorkItem(std::move(hreq), path, i->handler));
        assert(workQueue);
        if (workQueue->Enqueue(item.get()))
            item.release(); /* if true, queue took ownership */
        else {
            LogPrintf("WARNING: request rejected because http work queue depth exceeded, it can be increased with the -rpcworkqueue= setting\n");
            item->req->WriteReply(HTTP_INTERNAL, "Work queue depth exceeded");
        }
    } else {
        hreq->WriteReply(HTTP_NOTFOUND);
    }
}
Beispiel #14
0
static void
__out_start(struct chunk_transfer_s *ct)
{
	gint64 header_size = 0;
	int dst_fd;
	struct evkeyval *kv;
	struct evkeyvalq *src;
	GSList *dsts_out = NULL; /* struct evbuffer */
	GSList *lc = NULL;
	GSList *lb = NULL;
	struct timeval tv_read, tv_write;
	GSList *l = NULL;

	if (chunk_transfer_get_dst_status(ct) != CNX_NONE)
		return;
	GRID_DEBUG("Starting the output...");
	const char *content_length = chunk_transfer_find_req_header(ct, "Content-Length");

	ct->dst_size = ct->dst_size_remaining = g_ascii_strtoll(content_length, NULL, 10);
	if(chunk_transfer_get_target_rawx_count(ct) < 1) {
		GRID_ERROR("ERROR, no destination rawx...");	
		return;
	}

	GRID_DEBUG("ok we have targets, prepare to send data");

	for(l = ct->dst_rawx; l && l->data; l = l->next) {
		gchar dst[128];
		gchar port_str[16];
		guint16 port = 0;
		struct bufferevent* bevent = NULL;

		addr_info_get_addr(&(((service_info_t*)l->data)->addr), dst, sizeof(dst), &port);
		bzero(port_str, sizeof(port_str));
		g_snprintf(port_str, sizeof(port_str), "%d", port);


		GRID_DEBUG("addr extracted: %s %s", dst, port_str);

		dst_fd = tcpip_open(dst, port_str);

		GRID_DEBUG("Destination opened");
		/* ***** */
		bevent = bufferevent_socket_new(ct->evt_base, dst_fd, 0);
		GRID_DEBUG("buffer event created");
		tv_write.tv_sec = 3;
		tv_write.tv_usec = 0;
		tv_read.tv_sec = 3;
		tv_read.tv_usec = 0;
		bufferevent_set_timeouts(bevent, &tv_read, &tv_write);
		bufferevent_setcb(bevent, __dst_cb_in, __dst_cb_out, __dst_cb_error, ct);
		bufferevent_disable(bevent, EV_READ|EV_WRITE);

		/* Write the HTTP request and the grid headers */
		/* WARN: don't do prepend if you want to keep all in good order !! */
		dsts_out = g_slist_append(dsts_out, bufferevent_get_output(bevent));
		ct->dst_bevents = g_slist_append(ct->dst_bevents, bevent);
	}

	if(ct->dst_chunks) {
		GRID_DEBUG("dst_chunks filled, its ok");
	} else {
		GRID_DEBUG("no dst_chunks defined");
	}

	for (lc = ct->dst_chunks, lb = dsts_out; lc && lc->data && lb && lb->data; lc = lc->next, lb = lb->next) {
		gchar idstr[65];
		bzero(idstr, sizeof(idstr));
		container_id_to_string(lc->data, idstr, sizeof(idstr));

		evbuffer_add_printf(lb->data, "PUT /%s HTTP/1.0\r\n", idstr);
		GRID_DEBUG("Sending put order");
		/* Add missing headers (xattr not returned by rawx) */
		evbuffer_add_printf(lb->data, "chunkid: %s\n", idstr);
		evbuffer_add_printf(lb->data, "chunkpos: %"G_GUINT32_FORMAT"\n", ct->source_chunk->position);
		evbuffer_add_printf(lb->data, "chunksize: %"G_GINT64_FORMAT"\n", ct->source_chunk->size);
		/* container id str */
		gchar cidstr[65];
		bzero(cidstr, sizeof(cidstr));
		container_id_t cid;
		chunk_transfer_get_container_id(ct, cid);
		container_id_to_string(cid, cidstr, sizeof(cidstr));

		evbuffer_add_printf(lb->data, "containerid: %s\n", cidstr);
		evbuffer_add_printf(lb->data, "contentpath: %s\n", chunk_transfer_get_content_path(ct)); 
		evbuffer_add_printf(lb->data, "chunknb: %"G_GUINT32_FORMAT"\n", chunk_transfer_get_content_nb_chunks(ct)); 
		evbuffer_add_printf(lb->data, "contentsize: %"G_GINT64_FORMAT"\n", chunk_transfer_get_content_size(ct)); 
		if(ct->attrs->usr_metadata)
			evbuffer_add_printf(lb->data, "contentmetadata: %s\n", ct->attrs->usr_metadata); 
		if(ct->attrs->sys_metadata)
			evbuffer_add_printf(lb->data, "contentmetadata-sys: %s\n", ct->attrs->sys_metadata); 
		/* **** */

		evbuffer_add_printf(lb->data, "Connection: close\r\n");
		evbuffer_add_printf(lb->data, "Content-Type: application/octet-stream\r\n");
		evbuffer_add_printf(lb->data, "Content-Length: %"G_GINT64_FORMAT"\r\n", ct->dst_size);
	}

	src = ct->src_req->input_headers;
	TAILQ_FOREACH(kv, src, next) {
		GRID_DEBUG("headers found : %s | %s", kv->key, kv->value);
		if (g_str_has_prefix(kv->key, "X-Grid-")
				|| g_str_has_prefix(kv->key, "container")
				|| g_str_has_prefix(kv->key, "content")
				|| g_str_has_prefix(kv->key, "chunk")) {
			for(lb = dsts_out; lb && lb->data; lb = lb->next) {	
				evbuffer_add_printf(lb->data, "%s: %s\r\n", kv->key, kv->value);
			}
		}
	}
Beispiel #15
0
static void
bufferevent_readcb(evutil_socket_t fd, short event, void *arg)
{
	struct bufferevent *bufev = arg;
	struct bufferevent_private *bufev_p =
	    EVUTIL_UPCAST(bufev, struct bufferevent_private, bev);
	struct evbuffer *input;
	int res = 0;
	short what = BEV_EVENT_READING;
	ev_ssize_t howmuch = -1, readmax=-1;

	_bufferevent_incref_and_lock(bufev);

	if (event == EV_TIMEOUT) {
		what |= BEV_EVENT_TIMEOUT;
		goto error;
	}

	input = bufev->input;

	/*
	 * If we have a high watermark configured then we don't want to
	 * read more data than would make us reach the watermark.
	 */
	if (bufev->wm_read.high != 0) {
		howmuch = bufev->wm_read.high - evbuffer_get_length(input);
		/* we somehow lowered the watermark, stop reading */
		if (howmuch <= 0) {
			bufferevent_wm_suspend_read(bufev);
			goto done;
		}
	}
	readmax = _bufferevent_get_read_max(bufev_p);
	if (howmuch < 0 || howmuch > readmax) /* The use of -1 for "unlimited"
					       * uglifies this code. XXXX */
		howmuch = readmax;
	if (bufev_p->read_suspended)
		goto done;

	evbuffer_unfreeze(input, 0);
	res = evbuffer_read(input, fd, (int)howmuch); /* XXXX evbuffer_read would do better to take and return ev_ssize_t */
	evbuffer_freeze(input, 0);

	if (res == -1) {
		int err = evutil_socket_geterror(fd);
		if (EVUTIL_ERR_RW_RETRIABLE(err))
			goto reschedule;
		/* error case */
		what |= BEV_EVENT_ERROR;
	} else if (res == 0) {
		/* eof case */
		what |= BEV_EVENT_EOF;
	}

	if (res <= 0)
		goto error;

	_bufferevent_decrement_read_buckets(bufev_p, res);

	/* Invoke the user callback - must always be called last */
	if (evbuffer_get_length(input) >= bufev->wm_read.low)
		_bufferevent_run_readcb(bufev);

	goto done;

 reschedule:
	goto done;

 error:
	bufferevent_disable(bufev, EV_READ);
	_bufferevent_run_eventcb(bufev, what);

 done:
	_bufferevent_decref_and_unlock(bufev);
}
Beispiel #16
0
 void Connection::disable()
 {
     m_socket->shutdown();
     bufferevent_disable( m_handle, EV_READ | EV_WRITE );
 }
Beispiel #17
0
static void
bufferevent_writecb(evutil_socket_t fd, short event, void *arg)
{
	struct bufferevent *bufev = arg;
	struct bufferevent_private *bufev_p =
	    EVUTIL_UPCAST(bufev, struct bufferevent_private, bev);
	int res = 0;
	short what = BEV_EVENT_WRITING;
	int connected = 0;
	ev_ssize_t atmost = -1;

	_bufferevent_incref_and_lock(bufev);

	if (event == EV_TIMEOUT) {
		what |= BEV_EVENT_TIMEOUT;
		goto error;
	}
	if (bufev_p->connecting) {
		int c = evutil_socket_finished_connecting(fd);
		/* we need to fake the error if the connection was refused
		 * immediately - usually connection to localhost on BSD */
		if (bufev_p->connection_refused) {
		  bufev_p->connection_refused = 0;
		  c = -1;
		}

		if (c == 0)
			goto done;

		bufev_p->connecting = 0;
		if (c < 0) {
			event_del(&bufev->ev_write);
			event_del(&bufev->ev_read);
			_bufferevent_run_eventcb(bufev, BEV_EVENT_ERROR);
			goto done;
		} else {
			connected = 1;
#ifdef _WIN32
			if (BEV_IS_ASYNC(bufev)) {
				event_del(&bufev->ev_write);
				bufferevent_async_set_connected(bufev);
				_bufferevent_run_eventcb(bufev,
						BEV_EVENT_CONNECTED);
				goto done;
			}
#endif
			_bufferevent_run_eventcb(bufev,
					BEV_EVENT_CONNECTED);
			if (!(bufev->enabled & EV_WRITE) ||
			    bufev_p->write_suspended) {
				event_del(&bufev->ev_write);
				goto done;
			}
		}
	}

	atmost = _bufferevent_get_write_max(bufev_p);

	if (bufev_p->write_suspended)
		goto done;

	if (evbuffer_get_length(bufev->output)) {
		evbuffer_unfreeze(bufev->output, 1);
		res = evbuffer_write_atmost(bufev->output, fd, atmost);
		evbuffer_freeze(bufev->output, 1);
		if (res == -1) {
			int err = evutil_socket_geterror(fd);
			if (EVUTIL_ERR_RW_RETRIABLE(err))
				goto reschedule;
			what |= BEV_EVENT_ERROR;
		} else if (res == 0) {
			/* eof case
			   XXXX Actually, a 0 on write doesn't indicate
			   an EOF. An ECONNRESET might be more typical.
			 */
			what |= BEV_EVENT_EOF;
		}
		if (res <= 0)
			goto error;

		_bufferevent_decrement_write_buckets(bufev_p, res);
	}

	if (evbuffer_get_length(bufev->output) == 0) {
		event_del(&bufev->ev_write);
	}

	/*
	 * Invoke the user callback if our buffer is drained or below the
	 * low watermark.
	 */
	if ((res || !connected) &&
	    evbuffer_get_length(bufev->output) <= bufev->wm_write.low) {
		_bufferevent_run_writecb(bufev);
	}

	goto done;

 reschedule:
	if (evbuffer_get_length(bufev->output) == 0) {
		event_del(&bufev->ev_write);
	}
	goto done;

 error:
	bufferevent_disable(bufev, EV_WRITE);
	_bufferevent_run_eventcb(bufev, what);

 done:
	_bufferevent_decref_and_unlock(bufev);
}
Beispiel #18
0
static void cb_write(struct bufferevent *bev, void *arg)
{
	verbose(FIREHOSE, "%s(): Write exhausted\n", __func__);
	bufferevent_disable(bev, EV_WRITE);
	bufferevent_enable(bev, EV_READ);
}
Beispiel #19
0
// This starts a new TCP/TLS connection to notblocked
// It is called both when a new local proxy connection is accepted
// and when we need to re-open an existing telex transport (state->local is reused)
void make_new_telex_conn(struct telex_state *state)
{
    struct telex_conf *conf = state->conf;

    HexDump(LOG_TRACE, state->name, "Opening telex id:", state->remote_conn_id, sizeof(state->remote_conn_id));

	state->remotetcp = bufferevent_socket_new(state->base, -1,
		BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
	if (!state->remotetcp) {
		LogError(state->name, "Could not create remote bufferevent socket");
		StateCleanup(&state);
		return;
	}
	_inc(BEV);

    // TODO: make nonblocking lookup?
    bufferevent_socket_connect_hostname(state->remotetcp, NULL, AF_INET, conf->notblocked_host, conf->notblocked_port);

    // After resolution...
    /*
    struct sockaddr_in sin;
    if (getpeername(bufferevent_getfd(state->remotetcp), (struct sockaddr *)&sin, (socklen_t*)sizeof(sin)) < 0) {
        perror("getpeername");
        LogError("proxy", "getpeername failed");
        StateCleanup(&state);
        return;
    }
    char ip_p[INET_ADDRSTRLEN];
    LogTrace(state->name, "Connecting to %s:%d",
             evutil_inet_ntop(AF_INET, server_ip, ip_p, sizeof(ip_p)), state->conf->notblocked_port);
    //bufferevent_socket_connect(state->remotetcp, ai->ai_addr, (int)ai->ai_addrlen);
    */

	if (ssl_new_telex(state) < 0) {
		ssl_log_errors(LOG_ERROR, state->name);
		LogError(state->name, "Could not create new telex SSL connection object");
		StateCleanup(&state);
		return;
	}
	_inc(SSL);

	state->remote = bufferevent_openssl_filter_new(state->base, state->remotetcp,
		state->ssl, BUFFEREVENT_SSL_CONNECTING, BEV_OPT_DEFER_CALLBACKS);
		// Not BEV_OPT_CLOSE_ON_FREE!
	if (!state->remote) {
		LogError(state->name, "Could not create remote SSL bufferevent filter");
		StateCleanup(&state);
		return;
	}
	_inc(BEV);

    // First, set our read_cb to something that receives the SPTelex init message
	bufferevent_setcb(state->remote, (bufferevent_data_cb)first_read_cb, NULL,
		(bufferevent_event_cb)event_cb, state);

    // Disable until SPTelex init msg
    bufferevent_disable(state->local, EV_READ|EV_WRITE);
	bufferevent_setcb(state->local, (bufferevent_data_cb)read_cb, NULL,
		(bufferevent_event_cb)event_cb, state);

    // Hmm...we should make a second one of these
    state->in_local = 0;
}
Beispiel #20
0
static void httpc_read_cb(struct bufferevent *buffev, void *_arg)
{
	redsocks_client *client = _arg;

	assert(client->relay == buffev);
	assert(client->state == httpc_request_sent || client->state == httpc_reply_came);

	redsocks_touch_client(client);

	// evbuffer_add() triggers callbacks, so we can't write to client->client
	// till we know that we're going to ONFAIL_FORWARD_HTTP_ERR.
	// And the decision is made when all the headers are processed.
	struct evbuffer* tee = NULL;
	const bool do_errtee = client->instance->config.on_proxy_fail == ONFAIL_FORWARD_HTTP_ERR;

	if (client->state == httpc_request_sent) {
		size_t len = evbuffer_get_length(buffev->input);
		char *line = redsocks_evbuffer_readline(buffev->input);
		if (line) {
			unsigned int code;
			if (sscanf(line, "HTTP/%*u.%*u %u", &code) == 1) { // 1 == one _assigned_ match
				if (code == 407) { // auth failed
					http_auth *auth = red_http_auth(client->instance);

					if (auth->last_auth_query != NULL && auth->last_auth_count == 1) {
						redsocks_log_error(client, LOG_NOTICE, "HTTP Proxy auth failed: %s", line);
						client->state = httpc_no_way;
					} else if (client->instance->config.login == NULL || client->instance->config.password == NULL) {
						redsocks_log_error(client, LOG_NOTICE, "HTTP Proxy auth required, but no login/password configured: %s", line);
						client->state = httpc_no_way;
					} else {
						if (do_errtee)
							tee = evbuffer_new();
						char *auth_request = http_auth_request_header(buffev->input, tee);
						if (!auth_request) {
							redsocks_log_error(client, LOG_NOTICE, "HTTP Proxy auth required, but no <%s> header found: %s", auth_request_header, line);
							client->state = httpc_no_way;
						} else {
							free(line);
							if (tee)
								evbuffer_free(tee);
							free(auth->last_auth_query);
							char *ptr = auth_request;

							ptr += strlen(auth_request_header);
							while (isspace(*ptr))
								ptr++;

							size_t last_auth_query_len = strlen(ptr) + 1;
							auth->last_auth_query = calloc(last_auth_query_len, 1);
							memcpy(auth->last_auth_query, ptr, last_auth_query_len);
							auth->last_auth_count = 0;

							free(auth_request);

							if (bufferevent_disable(client->relay, EV_WRITE)) {
								redsocks_log_errno(client, LOG_ERR, "bufferevent_disable");
								return;
							}

							/* close relay tunnel */
							redsocks_bufferevent_free(client->relay);

							/* set to initial state*/
							client->state = httpc_new;

							/* and reconnect */
							redsocks_connect_relay(client);
							return;
						}
					}
				} else if (200 <= code && code <= 299) {
					client->state = httpc_reply_came;
				} else {
					redsocks_log_error(client, LOG_NOTICE, "HTTP Proxy error: %s", line);
					client->state = httpc_no_way;
				}
			} else {
				redsocks_log_error(client, LOG_NOTICE, "HTTP Proxy bad firstline: %s", line);
				client->state = httpc_no_way;
			}
			if (do_errtee && client->state == httpc_no_way) {
				if (bufferevent_write(client->client, line, strlen(line)) != 0 ||
				    bufferevent_write(client->client, "\r\n", 2) != 0)
				{
					redsocks_log_errno(client, LOG_NOTICE, "bufferevent_write");
					goto fail;
				}
			}
			free(line);
		}
		else if (len >= HTTP_HEAD_WM_HIGH) {
			redsocks_log_error(client, LOG_NOTICE, "HTTP Proxy reply is too long, %zu bytes", len);
			client->state = httpc_no_way;
		}
	}

	if (do_errtee && client->state == httpc_no_way) {
		if (tee) {
			if (bufferevent_write_buffer(client->client, tee) != 0) {
				redsocks_log_errno(client, LOG_NOTICE, "bufferevent_write_buffer");
				goto fail;
			}
		}
		redsocks_shutdown(client, client->client, SHUT_RD);
		const size_t avail = evbuffer_get_length(client->client->input);
		if (avail) {
			if (evbuffer_drain(client->client->input, avail) != 0) {
				redsocks_log_errno(client, LOG_NOTICE, "evbuffer_drain");
				goto fail;
			}
		}
		redsocks_shutdown(client, client->relay, SHUT_WR);
		client->state = httpc_headers_skipped;
	}

fail:
	if (tee) {
		evbuffer_free(tee);
	}

	if (client->state == httpc_no_way) {
		redsocks_drop_client(client);
		return;
	}

	while (client->state == httpc_reply_came) {
		char *line = redsocks_evbuffer_readline(buffev->input);
		if (line) {
			if (strlen(line) == 0) {
				client->state = httpc_headers_skipped;
			}
			free(line);
		}
		else {
			break;
		}
	}

	if (client->state == httpc_headers_skipped) {
		redsocks_start_relay(client);
	}
}
Beispiel #21
0
/**
 * @brief pauses a connection (disables reading)
 *
 * @param c a evhtp_connection_t * structure
 */
void
evhtp_connection_pause(evhtp_connection_t * c) {
    if ((bufferevent_get_enabled(c->bev) & EV_READ)) {
        bufferevent_disable(c->bev, EV_READ);
    }
}