-
Notifications
You must be signed in to change notification settings - Fork 0
/
sforward.c
359 lines (306 loc) · 8.37 KB
/
sforward.c
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
/*
* sforward - simple port forwarder for udp and tcp
* it was built as simple c program in one source file
* it can forward tcp and udp connections
* serves only one client. no multiclient mode
* uses select method
* all config lay in defines which is very useful for hiding
*/
#include <stdio.h> /* for printf() and fprintf() */
#include <sys/socket.h> /* for socket(), connect(), send(), and recv() */
#include <arpa/inet.h> /* for sockaddr_in and inet_addr() */
#include <stdlib.h> /* for atoi() and exit() */
#include <string.h> /* for memset() */
#include <unistd.h> /* for close() */
#include <fcntl.h> /* for fcntl() */
#define IPROTO_TCP 1
#define IPROTO_UDP 2
/* config */
#define INCOME_IP_ANY /* if defined then bind on all availaible interfaces */
#ifndef INCOME_IP_ANY
#define INCOME_IP "192.168.1.177" /* address to bind for incoming connection listen socket if no INCOME_IP_ANY */
#endif
#define INCOME_PORT 50100 /* port for incoming connection */
#define OUTCOME_IP "94.244.171.151" /* ip for outcome connection to connect to */
#define OUTCOME_PORT 8086 /* port for outcome connection */
#define PROTO IPROTO_UDP /* protocol to use. IPPROTO_TCP or IPPROTO_UDP */
#define DAEMON /* if defined then launch process as daemon */
#define VERBOSE 1 /* if defined produce more verbose output */
#define BUFFER_SIZE 1024*1024*128 /* temporary buffer size */
#define ONCE /* if defined then there only one connection can be made. then process die
else we just reopen listen socket and wait for another one */
/* logging */
#if VERBOSE > 0
#define vlog printf
#else
#define vlog printf
#endif
#define ilog printf
#define elog perror
/* create common buffer in statis memory for security reasons */
static char buffer[BUFFER_SIZE];
int connect_outcome(int outcome_sock) {
struct sockaddr_in sa;
/* set address */
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
if (inet_aton(OUTCOME_IP, &sa.sin_addr) <= 0) {
elog("can't convert outcome ip addr");
return -1;
}
sa.sin_port = htons(OUTCOME_PORT);
/* connect */
ilog("connection out");
if (connect(outcome_sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
elog("can't connect to outcome");
return -1;
}
ilog("successfuly connected to outboud");
return 0;
}
int bind_income_socket(int income_socket) {
struct sockaddr_in sa;
/* bind */
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
#ifdef INCOME_IP_ANY
sa.sin_addr.s_addr = INADDR_ANY;
#else
sa.sin_addr.s_addr = inet_addr(INCOME_IP);
#endif
sa.sin_port = htons(INCOME_PORT);
if (bind(income_socket, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
elog("can't bind income socket\n");
return -1;
}
return 0;
}
int traverse(int from_sock, int to_sock) {
size_t recv_size;
/* recv */
if ((recv_size = recv(from_sock, (void *)buffer, BUFFER_SIZE, 0)) <= 0) {
elog("error on recv in traverse\n");
return -1;
}
/* send */
if (send(to_sock, (void *)buffer, recv_size, 0) <= 0) {
elog("error on send in traverse\n");
return -1;
}
return 0;
}
int sock_loop(int income_sock, int outcome_sock) {
fd_set fdread;
fd_set fdwrite;
fd_set fdexcept;
fd_set *fds[3];
int fdmax;
int i;
/* fill fds */
fds[0] = &fdread;
fds[1] = &fdwrite;
fds[2] = &fdexcept;
/* set sockets to non block mode */
/*fcntl(income_sock, F_SETFL, O_NONBLOCK);
fcntl(outcome_sock, F_SETFL, O_NONBLOCK);*/
for(;;) {
/* select read sockets */
for (i = 0; i < 3; ++i) {
FD_ZERO(fds[i]);
FD_SET(income_sock, fds[i]);
FD_SET(outcome_sock, fds[i]);
}
fdmax = outcome_sock;
if (select(fdmax+1, &fdread, &fdwrite, &fdexcept, NULL) < 0) {
elog("select error in tcp_loop\n");
return -1;
}
/* if somebody closed the connection */
if (FD_ISSET(income_sock, &fdexcept)) { /*|| */
/* (FD_ISSET(income_sock, &fdread) && FD_ISSET(income_sock, &fdwrite))) {*/
elog("income sock closed\n");
break;
}
if (FD_ISSET(outcome_sock, &fdexcept)) { /* || */
/* (FD_ISSET(outcome_sock, &fdread) && FD_ISSET(outcome_sock, &fdwrite))) { */
elog("outcome sock closed\n");
break;
}
/* if we got data from income */
if (FD_ISSET(income_sock, &fdread)) {
vlog("got some data from income\n");
if (traverse(income_sock, outcome_sock) < 0) {
return -1;
}
}
/* if we got data from outcome */
if (FD_ISSET(outcome_sock, &fdread)) {
vlog("got some data from outcome");
if (traverse(outcome_sock, income_sock) < 0) {
return -1;
}
}
}
}
/*
* TCP related functions
*/
int get_tcp_listen_sock() {
int listen_sock;
struct sockaddr_in sa;
/* create socket */
listen_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listen_sock < 0) {
elog("can't create listen socket\n");
return -1;
}
bind_income_socket(listen_sock);
/* set to listen state */
if (listen(listen_sock, 1) < 0) {
elog("can't set listen_sock to listen mode\n");
return -1;
}
return listen_sock;
}
int get_tcp_client_sock(int listen_sock) {
int client_sock;
struct sockaddr_in sa;
int sa_len = sizeof(sa);
/* accept */
if ((client_sock = accept(listen_sock, (struct sockaddr *)&sa, &sa_len)) < 0) {
elog("can't accept new connection\n");
return -1;
}
vlog("got income client\n");
return client_sock;
}
int get_outcome_tcp_sock()
{
int outcome_sock;
/* create sock */
if ((outcome_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
elog("can't create outgoing socket\n");
return -1;
}
if(connect_outcome(outcome_sock) < 0) {
close(outcome_sock);
return -1;
}
return outcome_sock;
}
int start_tcp() {
int listen_sock;
int income_sock;
int outcome_sock;
int res = 0;
/* open listen connection */
ilog("opening listen connection\n");
if ((listen_sock = get_tcp_listen_sock()) < 0) {
res = -1;
goto cleanup;
}
/* accept client */
vlog("created listen sock : %d\n", listen_sock);
ilog("waiting for client to connect to income\n");
if ((income_sock = get_tcp_client_sock(listen_sock)) < 0) {
res = -1;
goto cleanup;
}
/* open outcome connection */
ilog("opening outcome connection\n");
if ((outcome_sock = get_outcome_tcp_sock()) < 0) {
res = -1;
goto cleanup;
}
/* fall into send & recv loop */
res = sock_loop(income_sock, outcome_sock);
/* cleanup */
cleanup:
close(income_sock);
close(outcome_sock);
return res;
}
/*
* UDP
*/
int get_udp_income_sock() {
int income_sock;
vlog("getting udp income socket\n");
income_sock = socket(AF_INET, SOCK_DGRAM, 0);
/* bind it */
if (bind_income_socket(income_sock) < 0) {
elog("can't bind income socket\n");
return -1;
}
return income_sock;
}
int get_udp_outcome_sock() {
int outcome_sock;
vlog("getting udp outcome socket\n");
if ((outcome_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
return -1;
connect_outcome(outcome_sock);
return outcome_sock;
}
int udp_connect_income_sock(int income_sock) {
/* here we wait for first udp packet and assume that the first packet is the connection initiation
* we just lost this packet cos this is udp
*/
struct sockaddr_in sa;
socklen_t sa_len = sizeof(sa);
vlog("connection udp income socket...\n");
if (recvfrom(income_sock, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&sa, &sa_len) < 0)
return -1;
ilog("udp income socket get packet and connecting now\n");
return connect(income_sock, (struct sockaddr *)&sa, sa_len);
}
int start_udp() {
int outcome_sock;
int income_sock;
int res = 0;
/* open udp income socket */
if ((income_sock = get_udp_income_sock()) < 0) {
res = -1;
goto cleanup;
}
/* listen for incoming packet on udp socket and connect*/
if (udp_connect_income_sock(income_sock) < 0) {
res = -1;
goto cleanup;
}
/* open udp outcome socket */
if ((outcome_sock = get_udp_outcome_sock()) < 0) {
res = -1;
goto cleanup;
}
/* fall into send & recv loop */
ilog("got udp connections and now fall into recv send loop\n");
res = sock_loop(income_sock, outcome_sock);
cleanup:
close(income_sock);
close(outcome_sock);
return res;
}
void daemonize() {
}
int main() {
#ifdef DAEMON
ilog("running in background\n");
daemonize();
#else
ilog("running in foreground\n");
#endif
#if PROTO == IPROTO_TCP
#ifndef ONCE
for (;;)
#endif
start_tcp();
#elif PROTO == IPROTO_UDP
#ifndef ONCE
for (;;)
#endif
start_udp();
#else
#error unsupported protocol selected. use IPPROTO_TCP or IPPROTO_UDP
#endif
}