forked from ravinet/mahimahi
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathprocess_recorder.cc
143 lines (109 loc) · 5.3 KB
/
process_recorder.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/* -*-mode:c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <net/route.h>
#include <functional>
#include "nat.hh"
#include "util.hh"
#include "interfaces.hh"
#include "address.hh"
#include "dns_proxy.hh"
#include "http_proxy.hh"
#include "netdevice.hh"
#include "event_loop.hh"
#include "socketpair.hh"
#include "process_recorder.hh"
#include "config.h"
using namespace std;
template <class StoreType>
int ProcessRecorder<StoreType>::record_process( std::function<int( FileDescriptor & )> && child_procedure,
Socket && socket_output,
const int & veth_counter,
const string & stdin_input )
{
TemporarilyRoot tr;
const Address nameserver = first_nameserver();
/* set egress and ingress ip addresses */
Interfaces interfaces;
auto egress_octet = interfaces.first_unassigned_address( 1 );
auto ingress_octet = interfaces.first_unassigned_address( egress_octet.second + 1 );
Address egress_addr = egress_octet.first, ingress_addr = ingress_octet.first;
/* make pair of devices */
const string unique_id = to_string( getpid() ) + "-" + to_string( veth_counter );
string egress_name = "veth-" + unique_id, ingress_name = "veth-i" + unique_id;
VirtualEthernetPair veth_devices( egress_name, ingress_name );
/* bring up egress */
assign_address( egress_name, egress_addr, ingress_addr );
/* create DNS proxy */
DNSProxy dns_outside( egress_addr, nameserver, nameserver );
/* set up NAT between egress and eth0 */
NAT nat_rule( ingress_addr );
/* set up http proxy for tcp */
HTTPProxy http_proxy( egress_addr );
/* set up dnat */
DNAT dnat( http_proxy.tcp_listener().local_addr(), egress_name );
/* prepare event loop */
EventLoop outer_event_loop;
/* Fork */
{
/* Make pipe for start signal */
auto signal_pipe = UnixDomainSocket::make_pair();
ChildProcess container_process( "recordshell", [&]() {
/* wait for the go signal */
signal_pipe.second.read();
/* bring up localhost */
interface_ioctl( Socket( UDP ), SIOCSIFFLAGS, "lo",
[] ( ifreq &ifr ) { ifr.ifr_flags = IFF_UP; } );
/* bring up veth device */
assign_address( ingress_name, ingress_addr, egress_addr );
/* create default route */
rtentry route;
zero( route );
route.rt_gateway = egress_addr.raw_sockaddr();
route.rt_dst = route.rt_genmask = Address().raw_sockaddr();
route.rt_flags = RTF_UP | RTF_GATEWAY;
SystemCall( "ioctl SIOCADDRT", ioctl( Socket( UDP ).num(), SIOCADDRT, &route ) );
/* create DNS proxy if nameserver address is local */
auto dns_inside = DNSProxy::maybe_proxy( nameserver,
dns_outside.udp_listener().local_addr(),
dns_outside.tcp_listener().local_addr() );
/* Fork again after dropping root privileges */
drop_privileges();
/* prepare child's event loop */
EventLoop shell_event_loop;
{
int pipefd[2];
SystemCall( "pipe", pipe( pipefd ) );
FileDescriptor read_end = pipefd[ 0 ], write_end = pipefd[ 1 ];
shell_event_loop.add_child_process( ChildProcess( "recorded_child", bind( child_procedure, std::ref( read_end ) ) ) );
/* Write out input to child, NOTE: Child might choose to ignore it. */
write_end.write( stdin_input );
/* pipe endpoint gets closed when FileDescriptor goes out of scope,
giving phantomjs EOF and telling it that the script is over */
}
if ( dns_inside ) {
dns_inside->register_handlers( shell_event_loop );
}
return shell_event_loop.loop();
}, true ); /* new network namespace */
/* give ingress to container */
run( { IP, "link", "set", "dev", ingress_name, "netns", to_string( container_process.pid() ) } );
veth_devices.set_kernel_will_destroy();
/* tell ChildProcess it's ok to proceed */
signal_pipe.first.write( "x" );
/* now that we have its pid, move container process to event loop */
outer_event_loop.add_child_process( ChildProcess( move( container_process ) ) );
}
/* do the actual recording in a different unprivileged child */
outer_event_loop.add_child_process( ChildProcess( "recorder", [&]() {
drop_privileges();
EventLoop recordr_event_loop;
dns_outside.register_handlers( recordr_event_loop );
http_proxy.register_handlers( recordr_event_loop, response_store_ );
auto ret = recordr_event_loop.loop();
response_store_.serialize_to_socket( move( socket_output ) );
return ret;
} ) );
return outer_event_loop.loop();
}