Initial commit
This commit is contained in:
commit
0881549cf2
1 changed files with 166 additions and 0 deletions
166
udp-proxy.c
Normal file
166
udp-proxy.c
Normal file
|
@ -0,0 +1,166 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define SESSION_TIMEOUT 120
|
||||
#define ENTRIES_SIZE 16
|
||||
|
||||
struct relay_entry {
|
||||
int relay_fd;
|
||||
struct sockaddr_in client_addr;
|
||||
time_t last_used;
|
||||
};
|
||||
|
||||
int server_fd;
|
||||
|
||||
int epoll_fd;
|
||||
|
||||
int key;
|
||||
|
||||
struct sockaddr_in dest_addr;
|
||||
|
||||
struct relay_entry relay_entries[ENTRIES_SIZE];
|
||||
|
||||
void recv_server_msg() {
|
||||
unsigned char buf[65536];
|
||||
struct sockaddr_in client_addr;
|
||||
socklen_t client_addr_len = sizeof(client_addr);
|
||||
ssize_t msg_len = recvfrom(server_fd, buf, sizeof(buf), 0, (struct sockaddr*) &client_addr, &client_addr_len);
|
||||
|
||||
struct relay_entry *entry = NULL;
|
||||
for (int i = 0; i < ENTRIES_SIZE; i ++) {
|
||||
if (relay_entries[i].relay_fd != -1
|
||||
&& client_addr.sin_addr.s_addr == relay_entries[i].client_addr.sin_addr.s_addr
|
||||
&& client_addr.sin_port == relay_entries[i].client_addr.sin_port) {
|
||||
entry = &relay_entries[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!entry) {
|
||||
int relay_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (relay_fd == -1) {
|
||||
perror("socket");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < ENTRIES_SIZE; i ++) {
|
||||
if (relay_entries[i].relay_fd == -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == ENTRIES_SIZE) {
|
||||
printf("%s:%d no more space.\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
|
||||
return;
|
||||
}
|
||||
|
||||
entry = &relay_entries[i];
|
||||
memcpy(&entry->client_addr, &client_addr, sizeof(client_addr));
|
||||
entry->relay_fd = relay_fd;
|
||||
|
||||
struct epoll_event event = {
|
||||
.events = EPOLLIN,
|
||||
.data = { .ptr = entry }
|
||||
};
|
||||
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, relay_fd, &event);
|
||||
|
||||
printf("%s:%d connected.\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
|
||||
}
|
||||
for (int i = 0; i < msg_len; i ++) {
|
||||
buf[i] ^= key;
|
||||
}
|
||||
sendto(entry->relay_fd, buf, msg_len, 0, (struct sockaddr *) &dest_addr, sizeof(dest_addr));
|
||||
entry->last_used = time(NULL);
|
||||
}
|
||||
|
||||
void recv_relay_msg(struct relay_entry *entry) {
|
||||
unsigned char buf[65536];
|
||||
ssize_t msg_len = recv(entry->relay_fd, buf, sizeof(buf), 0);
|
||||
for (int i = 0; i < msg_len; i ++) {
|
||||
buf[i] ^= key;
|
||||
}
|
||||
sendto(server_fd, buf, msg_len, 0, (struct sockaddr *) &entry->client_addr, sizeof(entry->client_addr));
|
||||
entry->last_used = time(NULL);
|
||||
}
|
||||
|
||||
void cleanup() {
|
||||
static int last_clean = 0;
|
||||
int cur_time = time(NULL);
|
||||
if (cur_time - last_clean >= SESSION_TIMEOUT) {
|
||||
for (int i = 0; i < ENTRIES_SIZE; i ++) {
|
||||
struct relay_entry *entry = &relay_entries[i];
|
||||
if (entry->relay_fd != -1 && cur_time - entry->last_used >= SESSION_TIMEOUT) {
|
||||
printf("%s:%d destroyed.\n", inet_ntoa(entry->client_addr.sin_addr), ntohs(entry->client_addr.sin_port));
|
||||
close(entry->relay_fd);
|
||||
entry->relay_fd = -1;
|
||||
}
|
||||
}
|
||||
last_clean = cur_time;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc < 6) {
|
||||
puts("usage: udp-proxy <listen-address> <listen-port> <remote-address> <remote-port> <key>");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
key = atoi(argv[5]);
|
||||
|
||||
for (int i = 0; i < ENTRIES_SIZE; i ++) {
|
||||
relay_entries[i].relay_fd = -1;
|
||||
}
|
||||
|
||||
server_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (server_fd == -1) {
|
||||
perror("socket");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct sockaddr_in server_addr = { .sin_family = AF_INET };
|
||||
inet_aton(argv[1], &server_addr.sin_addr);
|
||||
server_addr.sin_port = htons(atoi(argv[2]));
|
||||
|
||||
dest_addr.sin_family = AF_INET;
|
||||
inet_aton(argv[3], &dest_addr.sin_addr);
|
||||
dest_addr.sin_port = htons(atoi(argv[4]));
|
||||
|
||||
int res = bind(server_fd, (struct sockaddr*) &server_addr, sizeof(server_addr));
|
||||
if (res == -1) {
|
||||
perror("bind");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
epoll_fd = epoll_create1(0);
|
||||
if (epoll_fd == -1) {
|
||||
perror("epoll_create1");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct epoll_event event_filter = { .events = EPOLLIN };
|
||||
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event_filter);
|
||||
|
||||
struct epoll_event event;
|
||||
while (1) {
|
||||
res = epoll_wait(epoll_fd, &event, 1, SESSION_TIMEOUT * 1000);
|
||||
if (res == -1 && errno != EINTR) {
|
||||
perror("epoll_wait");
|
||||
exit(1);
|
||||
}
|
||||
if (res > 0) {
|
||||
if (event.data.ptr == NULL) {
|
||||
recv_server_msg();
|
||||
} else {
|
||||
recv_relay_msg(event.data.ptr);
|
||||
}
|
||||
}
|
||||
cleanup();
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue