Browse Source

Basic structure in place: can daemonise, listen on a Unix domain

socket, accept new connections, open a network interface using libpcap
and forward (all) traffic.
tags/v0.9.3
Michael Brown 19 years ago
parent
commit
4485df516c
1 changed files with 469 additions and 0 deletions
  1. 469
    0
      src/util/hijack.c

+ 469
- 0
src/util/hijack.c View File

@@ -0,0 +1,469 @@
1
+#include <stdio.h>
2
+#include <stdlib.h>
3
+#include <unistd.h>
4
+#include <string.h>
5
+#include <stdarg.h>
6
+#include <errno.h>
7
+#include <fcntl.h>
8
+#include <libgen.h>
9
+#include <signal.h>
10
+#include <net/if.h>
11
+#include <sys/select.h>
12
+#include <sys/socket.h>
13
+#include <sys/stat.h>
14
+#include <sys/un.h>
15
+#include <syslog.h>
16
+#include <getopt.h>
17
+#include <pcap.h>
18
+
19
+#define SNAPLEN 1600
20
+
21
+struct hijack {
22
+	pcap_t *pcap;
23
+	int fd;
24
+};
25
+
26
+struct hijack_listener {
27
+	struct sockaddr_un sun;
28
+	int fd;
29
+};
30
+
31
+struct hijack_options {
32
+	char interface[IF_NAMESIZE];
33
+	int daemonise;
34
+};
35
+
36
+static int daemonised = 0;
37
+
38
+/**
39
+ * Log error message
40
+ *
41
+ */
42
+static void logmsg ( int level, const char *format, ... ) {
43
+	va_list ap;
44
+
45
+	va_start ( ap, format );
46
+	if ( daemonised ) {
47
+		vsyslog ( ( LOG_DAEMON | level ), format, ap );
48
+	} else {
49
+		vfprintf ( stderr, format, ap );
50
+	}
51
+	va_end ( ap );
52
+}
53
+
54
+/**
55
+ * Open pcap device
56
+ *
57
+ */
58
+static int hijack_open ( const char *interface, struct hijack *hijack ) {
59
+	char errbuf[PCAP_ERRBUF_SIZE];
60
+
61
+	/* Open interface via pcap */
62
+	errbuf[0] = '\0';
63
+	hijack->pcap = pcap_open_live ( interface, SNAPLEN, 1, 0, errbuf );
64
+	if ( ! hijack->pcap ) {
65
+		logmsg ( LOG_ERR, "Failed to open %s: %s\n",
66
+			 interface, errbuf );
67
+		goto err;
68
+	}
69
+	if ( errbuf[0] )
70
+		logmsg ( LOG_WARNING, "Warning: %s\n", errbuf );
71
+
72
+	/* Set capture interface to non-blocking mode */
73
+	if ( pcap_setnonblock ( hijack->pcap, 1, errbuf ) < 0 ) {
74
+		logmsg ( LOG_ERR, "Could not make %s non-blocking: %s\n",
75
+			 interface, errbuf );
76
+		goto err;
77
+	}
78
+
79
+	/* Get file descriptor for select() */
80
+	hijack->fd = pcap_get_selectable_fd ( hijack->pcap );
81
+	if ( hijack->fd < 0 ) {
82
+		logmsg ( LOG_ERR, "Cannot get selectable file descriptor "
83
+			 "for %s\n", interface );
84
+		goto err;
85
+	}
86
+
87
+	return 0;
88
+
89
+ err:
90
+	if ( hijack->pcap )
91
+		pcap_close ( hijack->pcap );
92
+	return -1;
93
+}
94
+
95
+/**
96
+ * Close pcap device
97
+ *
98
+ */
99
+static void hijack_close ( struct hijack *hijack ) {
100
+	pcap_close ( hijack->pcap );
101
+}
102
+
103
+/**
104
+ * Forward data from hijacker
105
+ *
106
+ */
107
+static ssize_t forward_from_hijacker ( struct hijack *hijack, int fd ) {
108
+	char buf[SNAPLEN];
109
+	ssize_t len;
110
+
111
+	len = read ( fd, buf, sizeof ( buf ) );
112
+	if ( len < 0 ) {
113
+		logmsg ( LOG_ERR, "read from hijacker failed: %s\n",
114
+			 strerror ( errno ) );
115
+		return -1;
116
+	}
117
+
118
+	if ( len == 0 )
119
+		return 0;
120
+
121
+	if ( write ( hijack->fd, buf, len ) != len ) {
122
+		logmsg ( LOG_ERR, "write to hijacked port failed: %s\n",
123
+			 strerror ( errno ) );
124
+		return -1;
125
+	}
126
+
127
+	logmsg ( LOG_INFO, "forwarded %zd bytes from hijacker\n", len );
128
+
129
+	return len;
130
+};
131
+
132
+/**
133
+ * Forward data to hijacker
134
+ *
135
+ */
136
+static ssize_t forward_to_hijacker ( int fd, struct hijack *hijack ) {
137
+	char buf[SNAPLEN];
138
+	ssize_t len;
139
+
140
+	len = read ( hijack->fd, buf, sizeof ( buf ) );
141
+	if ( len < 0 ) {
142
+		logmsg ( LOG_ERR, "read from hijacked port failed: %s\n",
143
+			 strerror ( errno ) );
144
+		return -1;
145
+	}
146
+
147
+	if ( len == 0 )
148
+		return 0;
149
+
150
+	if ( write ( fd, buf, len ) != len ) {
151
+		logmsg ( LOG_ERR, "write to hijacker failed: %s\n",
152
+			 strerror ( errno ) );
153
+		return -1;
154
+	}
155
+
156
+	logmsg ( LOG_INFO, "forwarded %zd bytes to hijacker\n", len );
157
+
158
+	return len;
159
+};
160
+
161
+
162
+/**
163
+ * Run hijacker
164
+ *
165
+ */
166
+static int run_hijacker ( const char *interface, int fd ) {
167
+	struct hijack hijack;
168
+	fd_set fdset;
169
+	int max_fd;
170
+	ssize_t len;
171
+
172
+	memset ( &hijack, 0, sizeof ( hijack ) );
173
+
174
+	logmsg ( LOG_INFO, "new connection for %s\n", interface );
175
+
176
+	if ( hijack_open ( interface, &hijack ) < 0 )
177
+		goto err;
178
+	
179
+	/* Do the forwarding */
180
+	max_fd = ( ( fd > hijack.fd ) ? fd : hijack.fd );
181
+	while ( 1 ) {
182
+		/* Wait for available data */
183
+		FD_ZERO ( &fdset );
184
+		FD_SET ( fd, &fdset );
185
+		FD_SET ( hijack.fd, &fdset );
186
+		if ( select ( ( max_fd + 1 ), &fdset, NULL, NULL, 0 ) < 0 ) {
187
+			logmsg ( LOG_ERR, "select failed: %s\n",
188
+				 strerror ( errno ) );
189
+			goto err;
190
+		}
191
+		if ( FD_ISSET ( fd, &fdset ) ) {
192
+			len = forward_from_hijacker ( &hijack, fd );
193
+			if ( len < 0 )
194
+				goto err;
195
+			if ( len == 0 )
196
+				break;
197
+		}
198
+		if ( FD_ISSET ( hijack.fd, &fdset ) ) {
199
+			len = forward_to_hijacker ( fd, &hijack );
200
+			if ( len < 0 )
201
+				goto err;
202
+			if ( len == 0 )
203
+				break;
204
+		}
205
+	}
206
+
207
+	hijack_close ( &hijack );
208
+	logmsg ( LOG_INFO, "closed connection for %s\n", interface );
209
+
210
+	return 0;
211
+
212
+ err:
213
+	if ( hijack.pcap )
214
+		hijack_close ( &hijack );
215
+	return -1;
216
+}
217
+
218
+/**
219
+ * Open listener socket
220
+ *
221
+ */
222
+static int open_listener ( const char *interface,
223
+			   struct hijack_listener *listener ) {
224
+	
225
+	/* Create socket */
226
+	listener->fd = socket ( PF_UNIX, SOCK_SEQPACKET, 0 );
227
+	if ( listener->fd < 0 ) {
228
+		logmsg ( LOG_ERR, "Could not create socket: %s\n",
229
+			 strerror ( errno ) );
230
+		goto err;
231
+	}
232
+
233
+	/* Bind to local filename */
234
+	listener->sun.sun_family = AF_UNIX,
235
+	snprintf ( listener->sun.sun_path, sizeof ( listener->sun.sun_path ),
236
+		   "/var/run/hijack-%s", interface );
237
+	if ( bind ( listener->fd, ( struct sockaddr * ) &listener->sun,
238
+		    sizeof ( listener->sun ) ) < 0 ) {
239
+		logmsg ( LOG_ERR, "Could not bind socket to %s: %s\n",
240
+			 listener->sun.sun_path, strerror ( errno ) );
241
+		goto err;
242
+	}
243
+
244
+	/* Set as a listening socket */
245
+	if ( listen ( listener->fd, 0 ) < 0 ) {
246
+		logmsg ( LOG_ERR, "Could not listen to %s: %s\n",
247
+			 listener->sun.sun_path, strerror ( errno ) );
248
+		goto err;
249
+	}
250
+
251
+	return 0;
252
+	
253
+ err:
254
+	if ( listener->fd >= 0 )
255
+		close ( listener->fd );
256
+	return -1;
257
+}
258
+
259
+/**
260
+ * Listen on listener socket
261
+ *
262
+ */
263
+static int listen_for_hijackers ( struct hijack_listener *listener,
264
+				  const char *interface ) {
265
+	int fd;
266
+	pid_t child;
267
+	int rc;
268
+
269
+	logmsg ( LOG_INFO, "Listening on %s\n", listener->sun.sun_path );
270
+
271
+	while ( 1 ) {
272
+		/* Accept new connection */
273
+		fd = accept ( listener->fd, NULL, 0 );
274
+		if ( fd < 0 ) {
275
+			logmsg ( LOG_ERR, "accept failed: %s\n",
276
+				 strerror ( errno ) );
277
+			goto err;
278
+		}
279
+
280
+		/* Fork child process */
281
+		child = fork();
282
+		if ( child < 0 ) {
283
+			logmsg ( LOG_ERR, "fork failed: %s\n",
284
+				 strerror ( errno ) );
285
+			goto err;
286
+		}
287
+		if ( child == 0 ) {
288
+			/* I am the child; run the hijacker */
289
+			rc = run_hijacker ( interface, fd );
290
+			close ( fd );
291
+			exit ( rc );
292
+		}
293
+		
294
+		close ( fd );
295
+	}
296
+
297
+	return 0;
298
+
299
+ err:
300
+	return -1;
301
+}
302
+
303
+/**
304
+ * Close listener socket
305
+ *
306
+ */
307
+static void close_listener ( struct hijack_listener *listener ) {
308
+	close ( listener->fd );
309
+	unlink ( listener->sun.sun_path );
310
+}
311
+
312
+/**
313
+ * Print usage
314
+ *
315
+ */
316
+static void usage ( char **argv ) {
317
+	logmsg ( LOG_ERR,
318
+		 "Usage: %s [options]\n"
319
+		 "\n"
320
+		 "Options:\n"
321
+		 "  -h|--help               Print this help message\n"
322
+		 "  -i|--interface intf     Use specified network interface\n"
323
+		 "  -n|--nodaemon           Run in foreground\n",
324
+		 argv[0] );
325
+}
326
+
327
+/**
328
+ * Parse command-line options
329
+ *
330
+ */
331
+static int parse_options ( int argc, char **argv,
332
+			   struct hijack_options *options ) {
333
+	static struct option long_options[] = {
334
+		{ "interface", 1, NULL, 'i' },
335
+		{ "nodaemon", 0, NULL, 'n' },
336
+		{ "help", 0, NULL, 'h' },
337
+		{ },
338
+	};
339
+	int c;
340
+
341
+	/* Set default options */
342
+	memset ( options, 0, sizeof ( *options ) );
343
+	strncpy ( options->interface, "eth0", sizeof ( options->interface ) );
344
+	options->daemonise = 1;
345
+
346
+	/* Parse command-line options */
347
+	while ( 1 ) {
348
+		int option_index = 0;
349
+		
350
+		c = getopt_long ( argc, argv, "i:hn", long_options,
351
+				  &option_index );
352
+		if ( c < 0 )
353
+			break;
354
+
355
+		switch ( c ) {
356
+		case 'i':
357
+			strncpy ( options->interface, optarg,
358
+				  sizeof ( options->interface ) );
359
+			break;
360
+		case 'n':
361
+			options->daemonise = 0;
362
+			break;
363
+		case 'h':
364
+			usage( argv );
365
+			return -1;
366
+		case '?':
367
+			/* Unrecognised option */
368
+			return -1;
369
+		default:
370
+			logmsg ( LOG_ERR, "Unrecognised option '-%c'\n", c );
371
+			return -1;
372
+		}
373
+	}
374
+
375
+	/* Check there's nothing left over on the command line */
376
+	if ( optind != argc ) {
377
+		usage ( argv );
378
+		return -1;
379
+	}
380
+
381
+	return 0;
382
+}
383
+
384
+/**
385
+ * Daemonise
386
+ *
387
+ */
388
+static int daemonise ( const char *interface ) {
389
+	char pidfile[16 + IF_NAMESIZE + 4]; /* "/var/run/hijack-<intf>.pid" */
390
+	char pid[16];
391
+	int pidlen;
392
+	int fd = -1;
393
+
394
+	/* Daemonise */
395
+	if ( daemon ( 0, 0 ) < 0 ) {
396
+		logmsg ( LOG_ERR, "Could not daemonise: %s\n",
397
+			 strerror ( errno ) );
398
+		goto err;
399
+	}
400
+	daemonised = 1; /* Direct messages to syslog now */
401
+
402
+	/* Open pid file */
403
+	snprintf ( pidfile, sizeof ( pidfile ), "/var/run/hijack-%s.pid",
404
+		   interface );
405
+	fd = open ( pidfile, ( O_WRONLY | O_CREAT | O_TRUNC ),
406
+		    ( S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ) );
407
+	if ( fd < 0 ) {
408
+		logmsg ( LOG_ERR, "Could not open %s for writing: %s\n",
409
+			 pidfile, strerror ( errno ) );
410
+		goto err;
411
+	}
412
+
413
+	/* Write pid to file */
414
+	pidlen = snprintf ( pid, sizeof ( pid ), "%d\n", getpid() );
415
+	if ( write ( fd, pid, pidlen ) != pidlen ) {
416
+		logmsg ( LOG_ERR, "Could not write %s: %s\n",
417
+			 pidfile, strerror ( errno ) );
418
+		goto err;
419
+	}
420
+
421
+	close ( fd );
422
+	return 0;
423
+
424
+ err:
425
+	if ( fd >= 0 )
426
+		close ( fd );
427
+	return -1;
428
+}
429
+
430
+int main ( int argc, char **argv ) {
431
+	struct hijack_options options;
432
+	struct hijack_listener listener;
433
+	struct sigaction sigchld;
434
+
435
+	/* Parse command-line options */
436
+	if ( parse_options ( argc, argv, &options ) < 0 )
437
+		exit ( 1 );
438
+
439
+	/* Set up syslog connection */
440
+	openlog ( basename ( argv[0] ), LOG_PID, LOG_DAEMON );
441
+
442
+	/* Set up listening socket */
443
+	if ( open_listener ( options.interface, &listener ) < 0 )
444
+		exit ( 1 );
445
+
446
+	/* Daemonise on demand */
447
+	if ( options.daemonise ) {
448
+		if ( daemonise ( options.interface ) < 0 )
449
+			exit ( 1 );
450
+	}
451
+
452
+	/* Avoid creating zombies */
453
+	memset ( &sigchld, 0, sizeof ( sigchld ) );
454
+	sigchld.sa_handler = SIG_IGN;
455
+	sigchld.sa_flags = SA_NOCLDWAIT;
456
+	if ( sigaction ( SIGCHLD, &sigchld, NULL ) < 0 ) {
457
+		logmsg ( LOG_ERR, "Could not set signal handler: %s",
458
+			 strerror ( errno ) );
459
+		exit ( 1 );
460
+	}
461
+
462
+	/* Listen for hijackers */
463
+	if ( listen_for_hijackers ( &listener, options.interface ) < 0 )
464
+		exit ( 1 );
465
+
466
+	close_listener ( &listener );
467
+	
468
+	return 0;
469
+}

Loading…
Cancel
Save