static void
nproxy_conn_destroy(struct ev_loop *loop, struct nproxy_connection *conn)
{
	struct ev_uinet_ctx *ctx;

	ctx = conn->copy_watcher.ctx;
	ev_uinet_stop(loop, &conn->copy_watcher);
	ev_uinet_stop(loop, &conn->connected_watcher);
	ev_uinet_stop(loop, &conn->writable_watcher);
	uinet_soclose(ev_uinet_so(ctx));
	ev_uinet_detach(ctx);
}
static void
echo_connected_cb(struct ev_loop *loop, ev_uinet *w, int revents)
{
	struct echo_connection *conn = w->data;
	struct uinet_demo_echo *echo = conn->echo;
	struct uinet_sockaddr_in *sin1, *sin2;
	char buf1[32], buf2[32];
	int error;
	
	if (conn->verbose) {
		uinet_sogetsockaddr(w->so, (struct uinet_sockaddr **)&sin1);
		uinet_sogetpeeraddr(w->so, (struct uinet_sockaddr **)&sin2);
		printf("%s: connection %llu: established (local=%s:%u foreign=%s:%u)\n",
		       echo->cfg.name, (unsigned long long)conn->id,
		       uinet_inet_ntoa(sin1->sin_addr, buf1, sizeof(buf1)), ntohs(sin1->sin_port),
		       uinet_inet_ntoa(sin2->sin_addr, buf2, sizeof(buf2)), ntohs(sin2->sin_port));
		uinet_free_sockaddr((struct uinet_sockaddr *)sin1);
		uinet_free_sockaddr((struct uinet_sockaddr *)sin2);
	}

	if ((echo->cfg.copy_mode & UINET_IP_COPY_MODE_MAYBE) &&
	    ((uinet_sogetserialno(w->so) % echo->cfg.copy_every) == 0)){
		if ((error =
		     uinet_sosetcopymode(w->so, UINET_IP_COPY_MODE_RX|UINET_IP_COPY_MODE_ON,
					 echo->cfg.copy_limit, echo->cfg.copy_uif)))
			printf("%s: Failed to set copy mode (%d)\n",
			       echo->cfg.name, error);	
	}
	ev_uinet_stop(loop, w);
}
static void
nproxy_writable_cb(struct ev_loop *loop, ev_uinet *w, int revents)
{
	struct nproxy_connection *conn = w->data;
	struct nproxy_connection *other_side_conn = conn->other_side;

	/* restart the other side's copy watcher */
	ev_uinet_start(loop, &other_side_conn->copy_watcher);

	/* stop this watcher until the other side's copy watcher needs it again */
	ev_uinet_stop(loop, w);
}
Exemple #4
0
static void
destroy_conn(struct connection_context *conn)
{
	ev_uinet *w  = &conn->watcher;

	ev_uinet_stop(conn->server->loop, w);
	ev_uinet_detach(w->ctx);
	uinet_soclose(w->so);
#ifdef ENABLE_EXTRACT
	if (conn->buffer)
		free(conn->buffer);
	if (conn->parser)
		free(conn->parser);
#endif
	conn->server->interface->num_sockets--;
	free(conn);
}
static void
passive_connected_cb(struct ev_loop *loop, ev_uinet *w, int revents)
{
	struct passive_connection *conn = w->data;
	struct uinet_demo_passive *passive = conn->server;
	int error;

	if (passive->cfg.verbose)
		printf("%s: %s: connection established\n", passive->cfg.name, conn->label);

	if ((passive->cfg.copy_mode & UINET_IP_COPY_MODE_MAYBE) &&
	    ((uinet_sogetserialno(w->so) % passive->cfg.copy_every) == 0)){
		if ((error =
		     uinet_sosetcopymode(w->so, UINET_IP_COPY_MODE_RX|UINET_IP_COPY_MODE_ON,
					 passive->cfg.copy_limit, passive->cfg.copy_uif)))
			printf("%s: Failed to set copy mode (%d)\n",
			       passive->cfg.name, error);
	}
	ev_uinet_stop(loop, w);
}
static void
nproxy_outbound_connected_cb(struct ev_loop *loop, ev_uinet *w, int revents)
{
	struct nproxy_splice *splice = w->data;
	struct uinet_demo_nproxy *nproxy = splice->nproxy;
	struct uinet_sockaddr_in *sin1, *sin2;
	char buf1[32], buf2[32];

	ev_uinet_stop(loop, w);

	if (splice->verbose) {
		uinet_sogetsockaddr(w->so, (struct uinet_sockaddr **)&sin1);
		uinet_sogetpeeraddr(w->so, (struct uinet_sockaddr **)&sin2);
		printf("%s: splice %llu: outbound connection established (local=%s:%u foreign=%s:%u)\n",
		       nproxy->cfg.name, (unsigned long long)splice->id,
		       uinet_inet_ntoa(sin1->sin_addr, buf1, sizeof(buf1)), ntohs(sin1->sin_port),
		       uinet_inet_ntoa(sin2->sin_addr, buf2, sizeof(buf2)), ntohs(sin2->sin_port));
		uinet_free_sockaddr((struct uinet_sockaddr *)sin1);
		uinet_free_sockaddr((struct uinet_sockaddr *)sin2);
	}

	ev_uinet_start(loop, &splice->inbound.copy_watcher);
	ev_uinet_start(loop, &splice->outbound.copy_watcher);
}
static void
echo_cb(struct ev_loop *loop, ev_uinet *w, int revents)
{
	struct echo_connection *conn = w->data;
	struct uinet_demo_echo *echo = conn->echo;
	struct uinet_iovec iov;
	struct uinet_uio uio;
	int max_read;
	int max_write;
	int read_size;
	int error;
#define BUFFER_SIZE (64*1024)
	char buffer[BUFFER_SIZE];

	max_read = uinet_soreadable(w->so, 0);
	if (max_read <= 0) {
		/* the watcher should never be invoked if there is no error and there no bytes to be read */
		assert(max_read != 0);
		if (conn->verbose)
			printf("%s: connection %llu: can't read, closing\n",
			       echo->cfg.name, (unsigned long long)conn->id);
		goto err;
	} else {
		max_write = echo->sink ? max_read : uinet_sowritable(w->so, 0);
		if (-1 == max_write) {
			if (conn->verbose)
				printf("%s: connection %llu: can't write, closing\n",
				       echo->cfg.name, (unsigned long long)conn->id);
			goto err;
		} else {
			read_size = imin(imin(max_read, max_write), BUFFER_SIZE);

			uio.uio_iov = &iov;
			iov.iov_base = buffer;
			iov.iov_len = read_size;
			uio.uio_iovcnt = 1;
			uio.uio_offset = 0;
			uio.uio_resid = read_size;
	
			error = uinet_soreceive(w->so, NULL, &uio, NULL);
			if (0 != error) {
				printf("%s: connection %llu: read error (%d), closing\n",
				       echo->cfg.name, (unsigned long long)conn->id, error);
				goto err;
			}

			assert(uio.uio_resid == 0);

			if (!echo->sink) {
				uio.uio_iov = &iov;
				iov.iov_base = buffer;
				iov.iov_len = read_size;
				uio.uio_iovcnt = 1;
				uio.uio_offset = 0;
				uio.uio_resid = read_size;
				error = uinet_sosend(w->so, NULL, &uio, 0);
				if (0 != error) {
					printf("%s: connection %llu: write error (%d), closing\n",
					       echo->cfg.name, (unsigned long long)conn->id, error);
					goto err;
				}
			}

			if (max_write < max_read) {
				/* limited by write space, so change to a
				 * write watch on the socket, if we aren't
				 * already one.
				 */
				if (w->events & EV_READ) {
					ev_uinet_stop(loop, w);
					w->events = EV_WRITE;
					ev_uinet_start(loop, w);
				}
				/* else, continue as a write watch */
			} else if (!(w->events & EV_READ)) {
				/* write space wasn't a limitation this
				 * time, so switch back to waiting on
				 * EV_READ
				 */
				ev_uinet_stop(loop, w);
				w->events = EV_READ;
				ev_uinet_start(loop, w);
			}
			/* else, continue as a read watcher */
		}
	}

	return;

err:
	ev_uinet_stop(loop, w);
	ev_uinet_detach(w->ctx);
	uinet_soclose(w->so);
	free(conn);
}
Exemple #8
0
/*
 * The passive http extraction code works by alternately parsing the
 * passively reconstructed request and response streams.  The same callback
 * (below) is used to drive the parsing of each stream.  Parsing begins with
 * the request stream, and once a complete request has been parsed, the
 * parser and read watcher for the request stream are paused and the parser
 * and read watcher for the response stream are activated.  Once an entire
 * response is parsed, the parser and read watcher for the response stream
 * are paused, and the parser and read watcher for the request stream are
 * activated.  Along the way, response bodies that match the supplied list
 * of content types are extracted to files.
 *
 * This is example code whose purpose is to demonstrate upper layer protocol
 * processing using libuinet passive sockets functionality.  Little to no
 * attempt is made to deal with a number of ugly realities involved in
 * robustly parsing http streams in the wild.
 */
static void
passive_extract_cb(struct ev_loop *loop, ev_uinet *w, int revents)
{
	struct connection_context *conn = (struct connection_context *)w->data;
	struct uinet_iovec iov;
	struct uinet_uio uio;
	int max_read;
	int read_size;
	int bytes_read;
	int error;
	int flags;
	size_t nparsed;

	max_read = uinet_soreadable(w->so, 0);
	if (max_read <= 0) {
		/* the watcher should never be invoked if there is no error and there no bytes to be read */
		assert(max_read != 0);

		/*
		 * There are no more complete requests/responses to be had, shut everything down.
		 */
		if (conn->verbose)
			printf("%s: can't read, closing\n", conn->label);
		goto err;
	} else {
		read_size = imin(max_read, conn->buffer_size - conn->buffer_index);

		uio.uio_iov = &iov;
		iov.iov_base = &conn->buffer[conn->buffer_index];
		iov.iov_len = read_size;
		uio.uio_iovcnt = 1;
		uio.uio_offset = 0;
		uio.uio_resid = read_size;
		flags = UINET_MSG_HOLE_BREAK;

		error = uinet_soreceive(w->so, NULL, &uio, &flags);
		if (0 != error) {
			printf("%s: read error (%d), closing\n", conn->label, error);
			goto err;
		}

		if (flags & UINET_MSG_HOLE_BREAK) {
			printf("%s: hole in data, closing connections\n", conn->label);
			goto err;
		}

		bytes_read = read_size - uio.uio_resid;
		conn->buffer_count += bytes_read;
		conn->bytes_read += bytes_read;
		
		do {
			passive_extract_parse_buffer(conn);

			if (HTTP_PARSER_ERRNO(conn->parser) != HPE_OK) {
				if (HTTP_PARSER_ERRNO(conn->parser) == HPE_PAUSED) {
					if (conn->verbose > 1)
						printf("%s: completed parsing request or response\n", conn->label);
					http_parser_pause(conn->peer->parser, 0);
					passive_extract_parse_buffer(conn->peer);
					if (HTTP_PARSER_ERRNO(conn->peer->parser) == HPE_OK) {
						if (conn->verbose > 1)
							printf("%s: peer needs more data\n", conn->label);
						/* Peer parser needs more data */
						ev_uinet_stop(conn->server->loop, &conn->watcher);
						ev_uinet_start(conn->server->loop, &conn->peer->watcher);
						break;
					} else if (HTTP_PARSER_ERRNO(conn->peer->parser) != HPE_PAUSED) {
						printf("Peer parse failure %s, closing connections\n",
						       http_errno_name(HTTP_PARSER_ERRNO(conn->peer->parser)));
						goto err;
					} else {
						if (conn->verbose > 1)
							printf("%s: peer completed parsing request or response\n", conn->label);
						/*
						 * The other parser has paused, so it's time for us to continue
						 * parsing/receiving.
						 */
						http_parser_pause(conn->parser, 0);
					}
				} else {
					printf("Parse failure %s, closing connections\n",
					       http_errno_name(HTTP_PARSER_ERRNO(conn->parser)));
					goto err;
				}
			}
		} while (conn->buffer_count);
	}

	return;
err:
	/*
	 * Deliver EOS to each parser.  If a parser is paused or otherwise
	 * in an error state, no work will be done.  The main reason for
	 * doing this is to correctly handle the case where response parsing
	 * requires an EOS to complete.  Under such circumstances, one of
	 * the calls below will complete the work.
	 */
	http_parser_execute(conn->parser, conn->parser_settings, NULL, 0);
	http_parser_execute(conn->peer->parser, conn->peer->parser_settings, NULL, 0);

	destroy_conn(conn->peer);
	destroy_conn(conn);
}
static void
nproxy_inbound_connected_cb(struct ev_loop *loop, ev_uinet *w, int revents)
{
	struct nproxy_splice *splice = w->data;
	struct uinet_demo_nproxy *nproxy = splice->nproxy;
	struct uinet_sockaddr_in *sin_local, *sin_foreign;
	struct uinet_socket *outbound_socket = NULL;
	struct ev_uinet_ctx *soctx = NULL;
	struct uinet_in_l2info l2i;
	char buf1[32], buf2[32];
	int optlen, optval;
	int error;
	
	uinet_sogetsockaddr(w->so, (struct uinet_sockaddr **)&sin_local);
	uinet_sogetpeeraddr(w->so, (struct uinet_sockaddr **)&sin_foreign);
	if (splice->verbose)
		printf("%s: splice %llu: inbound connection established (local=%s:%u foreign=%s:%u)\n",
		       nproxy->cfg.name, (unsigned long long)splice->id,
		       uinet_inet_ntoa(sin_local->sin_addr, buf1, sizeof(buf1)), ntohs(sin_local->sin_port),
		       uinet_inet_ntoa(sin_foreign->sin_addr, buf2, sizeof(buf2)), ntohs(sin_foreign->sin_port));

	if ((nproxy->cfg.copy_mode & UINET_IP_COPY_MODE_MAYBE) &&
	    ((uinet_sogetserialno(w->so) % nproxy->cfg.copy_every) == 0)){
		if ((error =
		     uinet_sosetcopymode(w->so, UINET_IP_COPY_MODE_RX|UINET_IP_COPY_MODE_ON,
					 nproxy->cfg.copy_limit, nproxy->cfg.copy_uif)))
			printf("%s: splice %llu: Failed to set copy mode (%d)\n",
			       nproxy->cfg.name, (unsigned long long)splice->id, error);	
	}

	/* don't need this watcher anymore */
	ev_uinet_stop(loop, w);
	
	/* Create the outbound connection */
	error = uinet_socreate(nproxy->cfg.uinst, UINET_PF_INET, &outbound_socket, UINET_SOCK_STREAM, 0);
	if (error != 0) {
		printf("%s: splice %llu: outbound socket creation failed (%d)\n",
		       nproxy->cfg.name, (unsigned long long)splice->id, error);
		goto fail;
	}

	if ((error = uinet_make_socket_promiscuous(outbound_socket, nproxy->outbound_if))) {
		printf("%s: splice %llu: failed to make outbound socket promiscuous (%d)\n",
		       nproxy->cfg.name, (unsigned long long)splice->id, error);
		goto fail;
	}
	
	/*
	 * Socket needs to be non-blocking to work with the event system
	 */
	uinet_sosetnonblocking(outbound_socket, 1);

	optlen = sizeof(optval);
	optval = 1;
	error = uinet_sosetsockopt(outbound_socket, UINET_IPPROTO_TCP, UINET_TCP_NODELAY,
				   &optval, optlen);
	if (error != 0) {
		printf("%s: splice %llu: failed to set TCP_NODELAY on outbound socket (%d)\n",
		       nproxy->cfg.name, (unsigned long long)splice->id, error);
		goto fail;
	}

	/* Bind to the foreign address of the inbound connection */
	error = uinet_sobind(outbound_socket, (struct uinet_sockaddr *)sin_foreign);
	if (error != 0) {
		printf("%s: splice %llu: outbound socket bind failed (%d)\n",
		       nproxy->cfg.name, (unsigned long long)splice->id, error);
		goto fail;
	}

	/*
	 * Use the same MAC addrs and VLAN tag stack as the inbound
	 * connection, which requires swapping the local and foreign MAC
	 * addrs.
	 */
	error = uinet_getl2info(w->so, &l2i);
	if (error != 0) {
		printf("%s: splice %llu: unable to get l2info from inbound socket (%d)\n",
		       nproxy->cfg.name, (unsigned long long)splice->id, error);
		goto fail;
	}

	error = uinet_setl2info2(outbound_socket,
				 l2i.inl2i_foreign_addr, l2i.inl2i_local_addr,
				 l2i.inl2i_flags, &l2i.inl2i_tagstack);
	if (error != 0) {
		printf("%s: splice %llu: unable to set l2info for outbound socket (%d)\n",
		       nproxy->cfg.name, (unsigned long long)splice->id, error);
		goto fail;
	}

	soctx = ev_uinet_attach(outbound_socket);
	if (NULL == soctx) {
		printf("%s: splice %llu: failed to alloc libev context for outbound socket\n",
		       nproxy->cfg.name, (unsigned long long)splice->id);
		goto fail;
	}

	/* The connection target is the local address of the inbound connection */
	error = uinet_soconnect(outbound_socket, (struct uinet_sockaddr *)sin_local);
	if ((error != 0) && (error != UINET_EINPROGRESS)) {
		printf("%s: splice %llu: outbound socket connect failed (%d)\n",
		       nproxy->cfg.name, (unsigned long long)splice->id, error);
		goto fail;
	}
	
	uinet_free_sockaddr((struct uinet_sockaddr *)sin_local);
	uinet_free_sockaddr((struct uinet_sockaddr *)sin_foreign);

	ev_init(&splice->outbound.connected_watcher, nproxy_outbound_connected_cb);
	ev_uinet_set(&splice->outbound.connected_watcher, soctx, EV_WRITE);
	splice->outbound.connected_watcher.data = splice;
	ev_uinet_start(loop, &splice->outbound.connected_watcher);

	ev_init(&splice->outbound.writable_watcher, nproxy_writable_cb);
	ev_uinet_set(&splice->outbound.writable_watcher, soctx, EV_WRITE);
	splice->outbound.writable_watcher.data = &splice->outbound;
	/* will be started as necessary by the inbound copy watcher */
	
	ev_init(&splice->outbound.copy_watcher, nproxy_copy_cb);
	ev_uinet_set(&splice->outbound.copy_watcher, soctx, EV_READ);
	splice->outbound.copy_watcher.data = &splice->outbound;
	/* will be started when the outbound connection is established */
	
	return;

 fail:
	uinet_free_sockaddr((struct uinet_sockaddr *)sin_local);
	uinet_free_sockaddr((struct uinet_sockaddr *)sin_foreign);
	if (soctx) ev_uinet_detach(soctx);
	if (outbound_socket) uinet_soclose(outbound_socket);
	free(splice);
}
static void
nproxy_copy_cb(struct ev_loop *loop, ev_uinet *w, int revents)
{
	struct nproxy_connection *conn = w->data;
	struct nproxy_connection *other_side_conn = conn->other_side;
	struct nproxy_splice *splice = conn->splice;
	struct uinet_demo_nproxy *nproxy = splice->nproxy;
	struct uinet_socket *other_side_so = conn->other_side->copy_watcher.so;
	struct uinet_iovec iov;
	struct uinet_uio uio;
	int max_read;
	int max_write;
	int read_size;
	int error;
#define BUFFER_SIZE (64*1024)
	char buffer[BUFFER_SIZE];

	max_read = uinet_soreadable(w->so, 0);
	if (max_read <= 0) {
		/* the watcher should never be invoked if there is no error and there no bytes to be read */
		assert(max_read != 0);
		if (splice->verbose)
			printf("%s: splice %llu: %s: can't read, closing splice\n",
			       nproxy->cfg.name, (unsigned long long)splice->id, conn->name);
		goto err;
	} else {
		max_write = uinet_sowritable(other_side_so, 0);
		if (-1 == max_write) {
			if (splice->verbose)
				printf("%s: splice %llu: %s: can't write, closing splice\n",
				       nproxy->cfg.name, (unsigned long long)splice->id, other_side_conn->name);
			goto err;
		} else {
			read_size = imin(imin(max_read, max_write), BUFFER_SIZE);

			/* read_size == 0 should only happen when max_write is 0 */
			if (read_size > 0) {
				uio.uio_iov = &iov;
				iov.iov_base = buffer;
				iov.iov_len = read_size;
				uio.uio_iovcnt = 1;
				uio.uio_offset = 0;
				uio.uio_resid = read_size;
	
				error = uinet_soreceive(w->so, NULL, &uio, NULL);
				if (0 != error) {
					printf("%s: splice %llu: %s: read error (%d), closing splice\n",
					       nproxy->cfg.name, (unsigned long long)splice->id, conn->name, error);
					goto err;
				}

				assert(uio.uio_resid == 0);

				uio.uio_iov = &iov;
				iov.iov_base = buffer;
				iov.iov_len = read_size;
				uio.uio_iovcnt = 1;
				uio.uio_offset = 0;
				uio.uio_resid = read_size;
				error = uinet_sosend(other_side_so, NULL, &uio, 0);
				if (0 != error) {
					printf("%s: splice %llu: %s: write error (%d), closing splice\n",
					       nproxy->cfg.name, (unsigned long long)splice->id, other_side_conn->name, error);
					goto err;
				}
			}

			if (max_write < max_read) {
				/* 
				 * Limited by write space, so deactivate us
				 * and activate the write watcher for the
				 * other side, which will reactivate us when
				 * it fires.
				 */
				ev_uinet_stop(loop, w);
				ev_uinet_start(loop, &other_side_conn->writable_watcher);
			}
		}
	}

	return;

 err:
	nproxy_splice_destroy(loop, splice);
}