|
#include <stdlib.h> |
|
#include <stdio.h> |
|
#include <string.h> |
|
#include <pthread.h> |
|
#include <sys/types.h> |
|
#include <sys/socket.h> |
|
#include <netinet/in.h> |
|
#include <arpa/inet.h> |
|
#include <fcntl.h> |
|
#include <time.h> |
|
#include <unistd.h> |
|
#include <errno.h> |
|
|
|
#define PORT 14000 |
|
|
|
#define MAX_BUF_SIZE 2<<16 |
|
#define MAX_CONNECTION_NUM 1000 |
|
|
|
typedef struct CTX { |
|
unsigned int tcp_socketfd; |
|
struct sockaddr_in client_addr4; |
|
unsigned int thread_id; |
|
} CTX; |
|
|
|
void *echo_loop(void *arg) |
|
{ |
|
struct CTX *ctx = (CTX*)arg; |
|
int buf_size; |
|
char buf[MAX_BUF_SIZE]; |
|
struct sockaddr_in client_udp_sock_addr4; |
|
struct sockaddr_in server_udp_sock_addr4; |
|
int udp_socketfd; |
|
unsigned int addr_len = sizeof(client_udp_sock_addr4); |
|
unsigned short server_port, client_port; |
|
char str_addr[INET_ADDRSTRLEN]; |
|
unsigned short port; |
|
int flag; |
|
|
|
/* Create UDP socket */ |
|
udp_socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); |
|
if(udp_socketfd == -1) { |
|
perror("socket()"); |
|
goto end; |
|
} |
|
|
|
/* Set socket to reuse addresses */ |
|
flag = 1; |
|
setsockopt(udp_socketfd, SOL_SOCKET, SO_REUSEADDR, (const void*)&flag, (socklen_t)sizeof(flag)); |
|
|
|
/* Set up server address and port */ |
|
memset(&server_udp_sock_addr4, 0, sizeof(struct sockaddr_in)); |
|
server_udp_sock_addr4.sin_family = AF_INET; |
|
/* Server accept connection from any address */ |
|
server_udp_sock_addr4.sin_addr.s_addr = htonl(INADDR_ANY); |
|
/* Generate random port number */ |
|
server_udp_sock_addr4.sin_port = htons(0); |
|
|
|
/* Bind address and socket */ |
|
if( bind(udp_socketfd, (struct sockaddr*)&server_udp_sock_addr4, sizeof(server_udp_sock_addr4)) == -1 ) { |
|
perror("bind()"); |
|
goto end; |
|
} |
|
|
|
addr_len = sizeof(server_udp_sock_addr4); |
|
getsockname(udp_socketfd, (struct sockaddr *)&server_udp_sock_addr4, &addr_len); |
|
server_port = ntohs(server_udp_sock_addr4.sin_port); |
|
|
|
/* Try to receive port used by client */ |
|
if( recv(ctx->tcp_socketfd, &client_port, sizeof(unsigned short), 0) == -1 ) { |
|
perror("recv()"); |
|
goto end; |
|
} |
|
printf("Received port used by client: %d\n", client_port); |
|
|
|
/* Set up server address and port */ |
|
memset(&client_udp_sock_addr4, 0, sizeof(struct sockaddr_in)); |
|
client_udp_sock_addr4.sin_family = AF_INET; |
|
/* Server accept connection from any address */ |
|
client_udp_sock_addr4.sin_addr.s_addr = ctx->client_addr4.sin_addr.s_addr; |
|
/* Generate random port number */ |
|
client_udp_sock_addr4.sin_port = htons(client_port); |
|
|
|
/* Try to "connect" to this address ... server will be able to send and |
|
* receive packets only from this address. */ |
|
if(connect(udp_socketfd, (struct sockaddr *)&client_udp_sock_addr4, addr_len) == -1) { |
|
perror("connect()"); |
|
goto end; |
|
} |
|
|
|
/* Print address of client */ |
|
inet_ntop(AF_INET, &(client_udp_sock_addr4.sin_addr), str_addr, sizeof(str_addr)); |
|
port = ntohs(client_udp_sock_addr4.sin_port); |
|
printf("Connected with: %s:%d\n", str_addr, port); |
|
|
|
printf("Sending blind packet to the client\n"); |
|
/* Try to send blind message to client to simulate opening connection */ |
|
if( send(udp_socketfd, "0", 1, 0) == -1 ) { |
|
perror("send()"); |
|
goto end; |
|
} |
|
|
|
/* TODO: statefull firewall can send ICMP packet and it should be handled */ |
|
|
|
/* Send port number used by server to client */ |
|
printf("Sending server port to client: %d\n", server_port); |
|
if( send(ctx->tcp_socketfd, &server_port, sizeof(unsigned short), 0) == -1 ) { |
|
perror("send()"); |
|
goto end; |
|
} |
|
|
|
printf("Trying to receive UDP packet from the client ...\n"); |
|
|
|
/* Try to receive message from client over UDP */ |
|
buf_size = recv(udp_socketfd, buf, MAX_BUF_SIZE, 0); |
|
if(buf_size == -1 ) { |
|
if(errno != EHOSTUNREACH) { |
|
perror("recv()"); |
|
goto end; |
|
} else { |
|
/* Try to ignore received ICMP packet and try to call recv again */ |
|
buf_size = recv(udp_socketfd, buf, MAX_BUF_SIZE, 0); |
|
if(buf_size == -1) { |
|
perror("recv()"); |
|
goto end; |
|
} |
|
} |
|
} |
|
|
|
/* Try to send response to client over UDP */ |
|
buf[buf_size] = '\0'; |
|
printf("Received data: %s with len %d and sending response.\n", |
|
buf, buf_size); |
|
|
|
if( send(udp_socketfd, buf, buf_size, 0) == -1 ) { |
|
if(errno != EHOSTUNREACH) { |
|
perror("send()"); |
|
goto end; |
|
} else { |
|
/* Try to ignore received ICMP packet and call send() again */ |
|
if( send(udp_socketfd, buf, buf_size, 0) == -1 ) { |
|
perror("send()"); |
|
goto end; |
|
} |
|
} |
|
} |
|
|
|
printf("Existing thread %d\n", ctx->thread_id); |
|
|
|
end: |
|
free(ctx); |
|
pthread_exit(NULL); |
|
return NULL; |
|
} |
|
|
|
int main(void) |
|
{ |
|
int thread_id = 0; |
|
pthread_t threads[MAX_CONNECTION_NUM]; |
|
pthread_attr_t attr; |
|
int ret, flag; |
|
int tcp_socketfd, client_socketfd; |
|
struct sockaddr_in tcp_sock_addr4; |
|
struct sockaddr_in client_addr4; |
|
socklen_t addr_len; |
|
|
|
/* Create TCP socket */ |
|
tcp_socketfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); |
|
|
|
/* Set socket to reuse addresses */ |
|
flag = 1; |
|
setsockopt(tcp_socketfd, SOL_SOCKET, SO_REUSEADDR, (const void*)&flag, (socklen_t)sizeof(flag)); |
|
|
|
/* Set up server address and port */ |
|
memset(&tcp_sock_addr4, 0, sizeof(struct sockaddr_in)); |
|
tcp_sock_addr4.sin_family = AF_INET; |
|
/* Server accept connection from any address */ |
|
tcp_sock_addr4.sin_addr.s_addr = htonl(INADDR_ANY); |
|
/* Generate random port number */ |
|
tcp_sock_addr4.sin_port = htons(PORT); |
|
|
|
/* Bind address and socket */ |
|
if( bind(tcp_socketfd, (struct sockaddr*)&tcp_sock_addr4, sizeof(tcp_sock_addr4)) == -1) { |
|
perror("bind()"); |
|
return EXIT_FAILURE; |
|
} |
|
|
|
/* Listen for new connection */ |
|
ret = listen(tcp_socketfd, MAX_CONNECTION_NUM); |
|
if(ret == -1) { |
|
perror("listen()"); |
|
return EXIT_FAILURE; |
|
} |
|
|
|
/* TODO: join threads and free CTXs */ |
|
while(1) { |
|
printf("Waiting for new connection\n"); |
|
|
|
client_socketfd = accept(tcp_socketfd, (struct sockaddr*)&client_addr4, &addr_len); |
|
|
|
if(client_socketfd == -1) { |
|
perror("accept()"); |
|
return EXIT_FAILURE; |
|
} else { |
|
CTX *ctx = (CTX*)malloc(sizeof(CTX)); |
|
char client_hostname[INET_ADDRSTRLEN]; |
|
|
|
inet_ntop(AF_INET, &(client_addr4.sin_addr), client_hostname, INET_ADDRSTRLEN); |
|
printf("New connection from %s\n", client_hostname); |
|
|
|
ctx->tcp_socketfd = client_socketfd; |
|
memcpy(&ctx->client_addr4, &client_addr4, sizeof(struct sockaddr_in)); |
|
ctx->thread_id = ++thread_id; |
|
|
|
printf("Creating thread: %d\n", thread_id); |
|
pthread_attr_init(&attr); |
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); |
|
ret = pthread_create(&threads[thread_id], &attr, echo_loop, (void*)ctx); |
|
if(ret != 0) { |
|
perror("pthread_create()"); |
|
return EXIT_FAILURE; |
|
} |
|
} |
|
} |
|
|
|
return EXIT_SUCCESS; |
|
} |