• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2001-2003, Ximian, Inc.
4  */
5 
6 #include <stdlib.h>
7 #include <string.h>
8 
9 #include <libsoup/soup.h>
10 
11 /* WARNING: this is really really really not especially compliant with
12  * RFC 2616. But it does work for basic stuff.
13  */
14 
15 static SoupSession *session;
16 static SoupServer *server;
17 
18 typedef struct {
19 	GIOStream *iostream;
20 	GInputStream *istream;
21 	GOutputStream *ostream;
22 
23 	gssize nread, nwrote;
24 	guchar *buffer;
25 } TunnelEnd;
26 
27 typedef struct {
28 	SoupServer *self;
29 	SoupMessage *msg;
30 	SoupClientContext *context;
31 	GCancellable *cancellable;
32 
33 	TunnelEnd client, server;
34 } Tunnel;
35 
36 #define BUFSIZE 8192
37 
38 static void tunnel_read_cb (GObject      *object,
39 			    GAsyncResult *result,
40 			    gpointer      user_data);
41 
42 static void
tunnel_close(Tunnel * tunnel)43 tunnel_close (Tunnel *tunnel)
44 {
45 	if (tunnel->cancellable) {
46 		g_cancellable_cancel (tunnel->cancellable);
47 		g_object_unref (tunnel->cancellable);
48 	}
49 
50 	if (tunnel->client.iostream) {
51 		g_io_stream_close (tunnel->client.iostream, NULL, NULL);
52 		g_object_unref (tunnel->client.iostream);
53 	}
54 	if (tunnel->server.iostream) {
55 		g_io_stream_close (tunnel->server.iostream, NULL, NULL);
56 		g_object_unref (tunnel->server.iostream);
57 	}
58 
59 	g_free (tunnel->client.buffer);
60 	g_free (tunnel->server.buffer);
61 
62 	g_object_unref (tunnel->self);
63 	g_object_unref (tunnel->msg);
64 
65 	g_free (tunnel);
66 }
67 
68 static void
tunnel_wrote_cb(GObject * object,GAsyncResult * result,gpointer user_data)69 tunnel_wrote_cb (GObject      *object,
70 		 GAsyncResult *result,
71 		 gpointer      user_data)
72 {
73 	Tunnel *tunnel = user_data;
74 	TunnelEnd *write_end, *read_end;
75 	GError *error = NULL;
76 	gssize nwrote;
77 
78 	nwrote = g_output_stream_write_finish (G_OUTPUT_STREAM (object), result, &error);
79 	if (nwrote <= 0) {
80 		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
81 			g_error_free (error);
82 			return;
83 		} else if (error) {
84 			g_print ("Tunnel write failed: %s\n", error->message);
85 			g_error_free (error);
86 		}
87 		tunnel_close (tunnel);
88 		return;
89 	}
90 
91 	if (object == (GObject *)tunnel->client.ostream) {
92 		write_end = &tunnel->client;
93 		read_end = &tunnel->server;
94 	} else {
95 		write_end = &tunnel->server;
96 		read_end = &tunnel->client;
97 	}
98 
99 	write_end->nwrote += nwrote;
100 	if (write_end->nwrote < read_end->nread) {
101 		g_output_stream_write_async (write_end->ostream,
102 					     read_end->buffer + write_end->nwrote,
103 					     read_end->nread - write_end->nwrote,
104 					     G_PRIORITY_DEFAULT, tunnel->cancellable,
105 					     tunnel_wrote_cb, tunnel);
106 	} else {
107 		g_input_stream_read_async (read_end->istream,
108 					   read_end->buffer, BUFSIZE,
109 					   G_PRIORITY_DEFAULT, tunnel->cancellable,
110 					   tunnel_read_cb, tunnel);
111 	}
112 }
113 
114 static void
tunnel_read_cb(GObject * object,GAsyncResult * result,gpointer user_data)115 tunnel_read_cb (GObject      *object,
116 		GAsyncResult *result,
117 		gpointer      user_data)
118 {
119 	Tunnel *tunnel = user_data;
120 	TunnelEnd *read_end, *write_end;
121 	GError *error = NULL;
122 	gssize nread;
123 
124 	nread = g_input_stream_read_finish (G_INPUT_STREAM (object), result, &error);
125 	if (nread <= 0) {
126 		if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
127 			g_error_free (error);
128 			return;
129 		} else if (error) {
130 			g_print ("Tunnel read failed: %s\n", error->message);
131 			g_error_free (error);
132 		}
133 		tunnel_close (tunnel);
134 		return;
135 	}
136 
137 	if (object == (GObject *)tunnel->client.istream) {
138 		read_end = &tunnel->client;
139 		write_end = &tunnel->server;
140 	} else {
141 		read_end = &tunnel->server;
142 		write_end = &tunnel->client;
143 	}
144 
145 	read_end->nread = nread;
146 	write_end->nwrote = 0;
147 	g_output_stream_write_async (write_end->ostream,
148 				     read_end->buffer, read_end->nread,
149 				     G_PRIORITY_DEFAULT, tunnel->cancellable,
150 				     tunnel_wrote_cb, tunnel);
151 }
152 
153 static void
start_tunnel(SoupMessage * msg,gpointer user_data)154 start_tunnel (SoupMessage *msg, gpointer user_data)
155 {
156 	Tunnel *tunnel = user_data;
157 
158 	tunnel->client.iostream = soup_client_context_steal_connection (tunnel->context);
159 	tunnel->client.istream = g_io_stream_get_input_stream (tunnel->client.iostream);
160 	tunnel->client.ostream = g_io_stream_get_output_stream (tunnel->client.iostream);
161 
162 	tunnel->client.buffer = g_malloc (BUFSIZE);
163 	tunnel->server.buffer = g_malloc (BUFSIZE);
164 
165 	tunnel->cancellable = g_cancellable_new ();
166 
167 	g_input_stream_read_async (tunnel->client.istream,
168 				   tunnel->client.buffer, BUFSIZE,
169 				   G_PRIORITY_DEFAULT, tunnel->cancellable,
170 				   tunnel_read_cb, tunnel);
171 	g_input_stream_read_async (tunnel->server.istream,
172 				   tunnel->server.buffer, BUFSIZE,
173 				   G_PRIORITY_DEFAULT, tunnel->cancellable,
174 				   tunnel_read_cb, tunnel);
175 }
176 
177 
178 static void
tunnel_connected_cb(GObject * object,GAsyncResult * result,gpointer user_data)179 tunnel_connected_cb (GObject      *object,
180 		     GAsyncResult *result,
181 		     gpointer      user_data)
182 {
183 	Tunnel *tunnel = user_data;
184 	GError *error = NULL;
185 
186 	tunnel->server.iostream = (GIOStream *)
187 		g_socket_client_connect_to_host_finish (G_SOCKET_CLIENT (object), result, &error);
188 	if (!tunnel->server.iostream) {
189 		soup_message_set_status (tunnel->msg, SOUP_STATUS_BAD_GATEWAY);
190 		soup_message_set_response (tunnel->msg, "text/plain",
191 					   SOUP_MEMORY_COPY,
192 					   error->message, strlen (error->message));
193 		g_error_free (error);
194 		soup_server_unpause_message (tunnel->self, tunnel->msg);
195 		tunnel_close (tunnel);
196 		return;
197 	}
198 
199 	tunnel->server.istream = g_io_stream_get_input_stream (tunnel->server.iostream);
200 	tunnel->server.ostream = g_io_stream_get_output_stream (tunnel->server.iostream);
201 
202 	soup_message_set_status (tunnel->msg, SOUP_STATUS_OK);
203 	soup_server_unpause_message (tunnel->self, tunnel->msg);
204 	g_signal_connect (tunnel->msg, "finished",
205 			  G_CALLBACK (start_tunnel), tunnel);
206 }
207 
208 static void
try_tunnel(SoupServer * server,SoupMessage * msg,SoupClientContext * context)209 try_tunnel (SoupServer *server, SoupMessage *msg, SoupClientContext *context)
210 {
211 	Tunnel *tunnel;
212 	SoupURI *dest_uri;
213 	GSocketClient *sclient;
214 
215 	soup_server_pause_message (server, msg);
216 
217 	tunnel = g_new0 (Tunnel, 1);
218 	tunnel->self = g_object_ref (server);
219 	tunnel->msg = g_object_ref (msg);
220 	tunnel->context = context;
221 
222 	dest_uri = soup_message_get_uri (msg);
223 	sclient = g_socket_client_new ();
224 	g_socket_client_connect_to_host_async (sclient, dest_uri->host, dest_uri->port,
225 					       NULL, tunnel_connected_cb, tunnel);
226 	g_object_unref (sclient);
227 }
228 
229 static void
copy_header(const char * name,const char * value,gpointer dest_headers)230 copy_header (const char *name, const char *value, gpointer dest_headers)
231 {
232 	soup_message_headers_append (dest_headers, name, value);
233 }
234 
235 static void
send_headers(SoupMessage * from,SoupMessage * to)236 send_headers (SoupMessage *from, SoupMessage *to)
237 {
238 	g_print ("[%p] HTTP/1.%d %d %s\n", to,
239 		 soup_message_get_http_version (from),
240 		 from->status_code, from->reason_phrase);
241 
242 	soup_message_set_status_full (to, from->status_code,
243 				      from->reason_phrase);
244 	soup_message_headers_foreach (from->response_headers, copy_header,
245 				      to->response_headers);
246 	soup_message_headers_remove (to->response_headers, "Content-Length");
247 	soup_server_unpause_message (server, to);
248 }
249 
250 static void
send_chunk(SoupMessage * from,SoupBuffer * chunk,SoupMessage * to)251 send_chunk (SoupMessage *from, SoupBuffer *chunk, SoupMessage *to)
252 {
253 	g_print ("[%p]   writing chunk of %lu bytes\n", to,
254 		 (unsigned long)chunk->length);
255 
256 	soup_message_body_append_buffer (to->response_body, chunk);
257 	soup_server_unpause_message (server, to);
258 }
259 
260 static void
client_msg_failed(SoupMessage * msg,gpointer msg2)261 client_msg_failed (SoupMessage *msg, gpointer msg2)
262 {
263 	soup_session_cancel_message (session, msg2, SOUP_STATUS_IO_ERROR);
264 }
265 
266 static void
finish_msg(SoupSession * session,SoupMessage * msg2,gpointer data)267 finish_msg (SoupSession *session, SoupMessage *msg2, gpointer data)
268 {
269 	SoupMessage *msg = data;
270 
271 	g_print ("[%p]   done\n\n", msg);
272 	g_signal_handlers_disconnect_by_func (msg, client_msg_failed, msg2);
273 
274 	soup_message_body_complete (msg->response_body);
275 	soup_server_unpause_message (server, msg);
276 	g_object_unref (msg);
277 }
278 
279 static void
server_callback(SoupServer * server,SoupMessage * msg,const char * path,GHashTable * query,SoupClientContext * context,gpointer data)280 server_callback (SoupServer *server, SoupMessage *msg,
281 		 const char *path, GHashTable *query,
282 		 SoupClientContext *context, gpointer data)
283 {
284 	SoupMessage *msg2;
285 	char *uristr;
286 
287 	uristr = soup_uri_to_string (soup_message_get_uri (msg), FALSE);
288 	g_print ("[%p] %s %s HTTP/1.%d\n", msg, msg->method, uristr,
289 		 soup_message_get_http_version (msg));
290 
291 	if (msg->method == SOUP_METHOD_CONNECT) {
292 		try_tunnel (server, msg, context);
293 		return;
294 	}
295 
296         msg2 = soup_message_new (msg->method, uristr);
297 	soup_message_headers_foreach (msg->request_headers, copy_header,
298 				      msg2->request_headers);
299 	soup_message_headers_remove (msg2->request_headers, "Host");
300 	soup_message_headers_remove (msg2->request_headers, "Connection");
301 
302 	if (msg->request_body->length) {
303 		SoupBuffer *request = soup_message_body_flatten (msg->request_body);
304 		soup_message_body_append_buffer (msg2->request_body, request);
305 		soup_buffer_free (request);
306 	}
307 	soup_message_headers_set_encoding (msg->response_headers,
308 					   SOUP_ENCODING_CHUNKED);
309 
310 	g_signal_connect (msg2, "got_headers",
311 			  G_CALLBACK (send_headers), msg);
312 	g_signal_connect (msg2, "got_chunk",
313 			  G_CALLBACK (send_chunk), msg);
314 
315 	g_signal_connect (msg, "finished", G_CALLBACK (client_msg_failed), msg2);
316 
317 	soup_session_queue_message (session, msg2, finish_msg, msg);
318 
319 	g_object_ref (msg);
320 	soup_server_pause_message (server, msg);
321 }
322 
323 static gboolean
auth_callback(SoupAuthDomain * auth_domain,SoupMessage * msg,const char * username,const char * password,gpointer data)324 auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
325 	       const char *username, const char *password, gpointer data)
326 {
327 	return !strcmp (username, "user") && !strcmp (password, "password");
328 }
329 
330 static void
quit(int sig)331 quit (int sig)
332 {
333 	/* Exit cleanly on ^C in case we're valgrinding. */
334 	exit (0);
335 }
336 
337 static int port;
338 static gboolean require_auth;
339 
340 static GOptionEntry entries[] = {
341 	{ "auth-domain", 'a', 0,
342 	  G_OPTION_ARG_NONE, &require_auth,
343 	  "Require authentication", NULL },
344 	{ "port", 'p', 0,
345 	  G_OPTION_ARG_INT, &port,
346 	  "Port to listen on", NULL },
347 	{ NULL }
348 };
349 
350 int
main(int argc,char ** argv)351 main (int argc, char **argv)
352 {
353 	GOptionContext *opts;
354 	GMainLoop *loop;
355 	GSList *uris, *u;
356 	char *str;
357 	GError *error = NULL;
358 
359 	opts = g_option_context_new (NULL);
360 	g_option_context_add_main_entries (opts, entries, NULL);
361 	if (!g_option_context_parse (opts, &argc, &argv, &error)) {
362 		g_printerr ("Could not parse arguments: %s\n",
363 			    error->message);
364 		g_printerr ("%s",
365 			    g_option_context_get_help (opts, TRUE, NULL));
366 		exit (1);
367 	}
368 
369 	if (argc != 1) {
370 		g_printerr ("%s",
371 			    g_option_context_get_help (opts, TRUE, NULL));
372 		exit (1);
373 	}
374 	g_option_context_free (opts);
375 
376 	signal (SIGINT, quit);
377 
378 	server = g_object_new (SOUP_TYPE_SERVER, NULL);
379 	soup_server_add_handler (server, NULL,
380 				 server_callback, NULL, NULL);
381 	if (require_auth) {
382 		SoupAuthDomain *auth_domain;
383 
384 		auth_domain = soup_auth_domain_basic_new (
385 			SOUP_AUTH_DOMAIN_REALM, "simple-proxy",
386 			SOUP_AUTH_DOMAIN_PROXY, TRUE,
387 			SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, auth_callback,
388 			NULL);
389 		soup_server_add_auth_domain (server, auth_domain);
390 		g_object_unref (auth_domain);
391 	}
392 
393 	soup_server_listen_all (server, port, 0, &error);
394 	if (error) {
395 		g_printerr ("Unable to create server: %s\n", error->message);
396 		exit (1);
397 	}
398 
399 	uris = soup_server_get_uris (server);
400 	for (u = uris; u; u = u->next) {
401 		str = soup_uri_to_string (u->data, FALSE);
402 		g_print ("Listening on %s\n", str);
403 		g_free (str);
404 		soup_uri_free (u->data);
405 	}
406 	g_slist_free (uris);
407 
408 	session = soup_session_new ();
409 
410 	g_print ("\nWaiting for requests...\n");
411 
412 	loop = g_main_loop_new (NULL, TRUE);
413 	g_main_loop_run (loop);
414 	g_main_loop_unref (loop);
415 
416 	return 0;
417 }
418