diff options
Diffstat (limited to 'src/server/binding.c')
-rw-r--r-- | src/server/binding.c | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/src/server/binding.c b/src/server/binding.c new file mode 100644 index 0000000..47c62c6 --- /dev/null +++ b/src/server/binding.c @@ -0,0 +1,245 @@ +#include <netinet/in.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +#include "addr.h" +#include "binding.h" +#include "../io/log.h" + +static void create_udp_binding(UdpSocket* socket, uint16_t port) { + if (create_udp_socket(V6, socket) < 0) { + ERROR("Failed to create UDP socket: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + + IpAddr addr; + ip_addr_any6(&addr); + + SocketAddr socketaddr; + create_socket_addr(port, addr, &socketaddr); + + if (bind_udp_socket(&socketaddr, socket) < 0) { + ERROR("Failed to bind UDP socket on port %hu: %s", port, strerror(errno)); + exit(EXIT_FAILURE); + } +} + +static void create_tcp_binding(TcpSocket* socket, uint16_t port) { + if (create_tcp_socket(V6, socket) < 0) { + ERROR("Failed to create TCP socket: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + + IpAddr addr; + ip_addr_any6(&addr); + + SocketAddr socketaddr; + create_socket_addr(port, addr, &socketaddr); + + if (bind_tcp_socket(&socketaddr, socket) < 0) { + ERROR("Failed to bind TCP socket on port %hu: %s", port, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (listen_tcp_socket(socket, 5) < 0) { + ERROR("Failed to listen on TCP socket: %s", strerror(errno)); + exit(EXIT_FAILURE); + } +} + +void create_binding(BindingType type, uint16_t port, Binding* binding) { + binding->type = type; + if (type == UDP) { + create_udp_binding(&binding->sock.udp, port); + } else if(type == TCP) { + create_tcp_binding(&binding->sock.tcp, port); + } else { + exit(EXIT_FAILURE); + } +} + +void free_binding(Binding* binding) { + if (binding->type == UDP) { + close_udp_socket(&binding->sock.udp); + } else if(binding->type == TCP) { + close_tcp_socket(&binding->sock.tcp); + } +} + +bool accept_connection(Binding* binding, Connection* connection) { + connection->type = binding->type; + + if(binding->type == UDP) { + connection->sock.udp.udp = binding->sock.udp; + memset(&connection->sock.udp.clientaddr, 0, sizeof(SocketAddr)); + return true; + } + + if (accept_tcp_socket(&binding->sock.tcp, &connection->sock.tcp) < 0) { + ERROR("Failed to accept TCP connection: %s", strerror(errno)); + return false; + } + + return true; +} + +static void read_to_packet(uint8_t* buf, uint16_t len, Packet* packet) { + PacketBuffer* pkbuffer = buffer_create(len); + for (int i = 0; i < len; i++) { + buffer_write(pkbuffer, buf[i]); + } + buffer_seek(pkbuffer, 0); + read_packet(pkbuffer, packet); + buffer_free(pkbuffer); +} + +static bool read_udp(Connection* connection, Packet* packet) { + uint8_t buffer[512]; + int32_t n = read_udp_socket( + &connection->sock.udp.udp, + buffer, + 512, + &connection->sock.udp.clientaddr + ); + if (n < 0) { + return false; + } + read_to_packet(buffer, n, packet); + return true; +} + +static bool read_tcp(Connection* connection, Packet* packet) { + uint16_t len; + if ( read_tcp_stream( + &connection->sock.tcp, + &len, + sizeof(uint16_t) + ) < 0) { + return false; + } + + uint8_t buffer[len]; + if ( read_tcp_stream( + &connection->sock.tcp, + buffer, + len + ) < 0) { + return false; + } + + read_to_packet(buffer, len, packet); + return true; +} + +bool read_connection(Connection* connection, Packet* packet) { + if (connection->type == UDP) { + return read_udp(connection, packet); + } else if (connection->type == TCP) { + return read_tcp(connection, packet); + } + return false; +} + +static bool write_udp(Connection* connection, uint8_t* buf, uint16_t len) { + //if (len > 512) { + buf[2] = buf[2] | 0x03; + // len = 512; + // } + return write_udp_socket( + &connection->sock.udp.udp, + buf, + len, + &connection->sock.udp.clientaddr + ) == len; +} + +static bool write_tcp(Connection* connection, uint8_t* buf, uint16_t len) { + len = htons(len); + if (write_tcp_stream( + &connection->sock.tcp, + &len, + sizeof(uint16_t) + ) < 0) { + return false; + } + + if (write_tcp_stream( + &connection->sock.tcp, + buf, + len + ) < 0) { + return false; + } + + return true; +} + +bool write_connection(Connection* connection, Packet* packet) { + PacketBuffer* pkbuffer = buffer_create(64); + write_packet(pkbuffer, packet); + uint16_t len = buffer_get_size(pkbuffer); + uint8_t* buffer = buffer_get_ptr(pkbuffer); + bool success = false; + if(connection->type == UDP) { + success = write_udp(connection, buffer, len); + } else if(connection->type == TCP) { + success = write_tcp(connection, buffer, len); + }; + buffer_free(pkbuffer); + return success; +} + +void free_connection(Connection* connection) { + if (connection->type == TCP) { + close_tcp_stream(&connection->sock.tcp); + } +} + +static bool create_udp_request(SocketAddr* addr, Connection* request) { + if ( create_udp_socket(addr->type, &request->sock.udp.udp) < 0) { + ERROR("Failed to connect to UDP socket: %s", strerror(errno)); + return false; + } + request->sock.udp.clientaddr = *addr; + return true; +} + +static bool create_tcp_request(SocketAddr* addr, Connection* request) { + if( connect_tcp_stream(addr, &request->sock.tcp) < 0) { + ERROR("Failed to connect to TCP socket: %s", strerror(errno)); + return false; + } + return true; +} + +bool create_request(BindingType type, SocketAddr* addr, Connection* request) { + request->type = type; + if (type == UDP) { + return create_udp_request(addr, request); + } else if (type == TCP) { + return create_tcp_request(addr, request); + } else { + return true; + } +} + +bool request_packet(Connection* request, Packet* in, Packet* out) { + if (!write_connection(request, in)) { + return false; + } + if (!read_connection(request, out)) { + return false; + } + return true; +} + +void free_request(Connection* connection) { + if (connection->type == UDP) { + close_udp_socket(&connection->sock.udp.udp); + } else if (connection->type == TCP) { + close_tcp_stream(&connection->sock.tcp); + } +} |