commit 72f97117e73bee6ec6785245e615405abe7d5538 Author: Masahiko AMANO Date: Fri Oct 18 20:28:30 2024 +0300 init diff --git a/aiken.c b/aiken.c new file mode 100644 index 0000000..14ce11d --- /dev/null +++ b/aiken.c @@ -0,0 +1,318 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PORT_UDP 46226 +#define PORT_TCP 46227 + +#define BUF_SIZE 1024 + +char *basename(const char *path) { + char *res = strrchr(path, '/'); + return (res == NULL) ? path : (res + 1); +} + +void token_generate(char *dst) { + sprintf(dst, "%04x", (uint16_t) time(NULL)); +} + +void share(const char *path) { + FILE *file = fopen(path, "rb"); + if (file == NULL) { + perror("Failed to open file"); + exit(errno); + } + char token[5]; + int udp_sock = socket(AF_INET, SOCK_DGRAM, 0), sock_option = 1; + if (udp_sock < 0) { + perror("Failed to initialize UDP socket"); + fclose(file); + exit(errno); + } + if (setsockopt(udp_sock, SOL_SOCKET, SO_REUSEADDR, &sock_option, sizeof(sock_option)) < 0) { + perror("Failed to set UDP socket options"); + exit(errno); + } + struct sockaddr_in listen_addr, receiver_addr; + socklen_t addr_len = sizeof(receiver_addr); + bzero(&listen_addr, sizeof(listen_addr)); + listen_addr.sin_family = AF_INET; + listen_addr.sin_addr.s_addr = INADDR_ANY; + listen_addr.sin_port = htons(PORT_UDP); + if (bind(udp_sock, (struct sockaddr *) &listen_addr, sizeof(listen_addr)) < 0) { + perror("Failed to bind UDP socket"); + fclose(file); + close(udp_sock); + exit(errno); + } + token_generate(token); + printf("Successfully shared!\nToken: %s\n\n", token); + char buffer[BUF_SIZE]; + char *filename = basename(path); + int tcp_server_sock, tcp_client_sock; + printf("Waiting for request...\n"); + // recieve request from receiver + if (recvfrom(udp_sock, buffer, 4, 0, (struct sockaddr *) &receiver_addr, &addr_len) < 4) { + printf("Got invalid request from %s\n", inet_ntoa(receiver_addr.sin_addr)); + fclose(file); + close(udp_sock); + exit(errno); + } + // check request token + if (memcmp(buffer, token, 4) != 0) { + printf("Got invalid token from %s\n", inet_ntoa(receiver_addr.sin_addr)); + // send NO packet which tells that request is rejected + if (sendto(udp_sock, "NO", 2, 0, (struct sockaddr *) &receiver_addr, addr_len) < 2) { + perror("Failed to send NO packet"); + } + fclose(file); + close(udp_sock); + exit(errno); + } + printf("Got request from %s\n", inet_ntoa(receiver_addr.sin_addr)); + // create a TCP socket to send file + tcp_server_sock = socket(AF_INET, SOCK_STREAM, 0); + if (tcp_server_sock < 0) { + perror("Failed to initialize TCP socket"); + fclose(file); + close(udp_sock); + exit(errno); + } + if (setsockopt(tcp_server_sock, SOL_SOCKET, SO_REUSEADDR, &sock_option, sizeof(sock_option)) < 0) { + perror("Failed to set TCP socket options"); + exit(errno); + } + listen_addr.sin_port = htons(PORT_TCP); + if (bind(tcp_server_sock, (struct sockaddr *) &listen_addr, addr_len) < 0) { + perror("Failed to bind TCP socket"); + fclose(file); + close(udp_sock); + close(tcp_server_sock); + exit(errno); + } + if (listen(tcp_server_sock, 1) < 0) { + perror("Failed to listen to TCP socket"); + fclose(file); + close(udp_sock); + close(tcp_server_sock); + exit(errno); + } + // send OK packet which tells that we are ready to send file + if (sendto(udp_sock, "OK", 2, 0, (struct sockaddr *) &receiver_addr, addr_len) < 2) { + perror("Failed to send OK packet"); + fclose(file); + close(udp_sock); + close(tcp_server_sock); + exit(errno); + } + close(udp_sock); + printf("Waiting for reciever connection...\n"); + // wait for receiver connection + if ((tcp_client_sock = accept(tcp_server_sock, (struct sockaddr *) &receiver_addr, &addr_len)) < 0) { + perror("Failed to accept receiver connection"); + fclose(file); + close(tcp_server_sock); + exit(errno); + } + printf("Receiver connected, sending...\n"); + // send file name + if (send(tcp_client_sock, filename, strlen(filename) + 1, 0) < strlen(filename) + 1) { + perror("Failed to send file name"); + fclose(file); + close(tcp_client_sock); + close(tcp_server_sock); + exit(errno); + } + // send file data + ssize_t rcount; + while ((rcount = fread(buffer, 1, BUF_SIZE, file)) > 0) { + if (send(tcp_client_sock, buffer, rcount, 0) != rcount) { + perror("Failed to send file data"); + fclose(file); + close(tcp_client_sock); + close(tcp_server_sock); + exit(errno); + } + } + close(tcp_client_sock); + close(tcp_server_sock); + printf("Sending complete!\n"); +} + +void get(const char *token, const char *path) { + size_t abspath_len = strlen(path) + 1; + if (abspath_len > PATH_MAX) { + printf("Invalid path: too long\n"); + exit(1); + } + char abspath[PATH_MAX]; + strcpy(abspath, path); + char fname[FILENAME_MAX]; + size_t fname_len; + struct stat stat_buf; + int udp_sock = socket(AF_INET, SOCK_DGRAM, 0); + if (udp_sock < 0) { + perror("Failed to initialize UDP socket"); + exit(errno); + } + int sock_option = 1; + if (setsockopt(udp_sock, SOL_SOCKET, SO_BROADCAST, &sock_option, sizeof(sock_option)) < 0) { + perror("Failed to set UDP socket options"); + exit(errno); + } + struct sockaddr_in broadcast_addr, server_addr; + socklen_t addr_len = sizeof(server_addr); + bzero(&broadcast_addr, sizeof(broadcast_addr)); + broadcast_addr.sin_family = AF_INET; + broadcast_addr.sin_addr.s_addr = INADDR_BROADCAST; + broadcast_addr.sin_port = htons(PORT_UDP); + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = INADDR_ANY; + server_addr.sin_port = htons(PORT_TCP); + int tcp_sock = socket(AF_INET, SOCK_STREAM, 0); + if (tcp_sock < 0) { + perror("Failed to initialize TCP socket"); + exit(errno); + } + if (setsockopt(tcp_sock, SOL_SOCKET, SO_REUSEADDR, &sock_option, sizeof(sock_option)) < 0) { + perror("Failed to set TCP socket options"); + exit(errno); + } + if (bind(tcp_sock, (struct sockaddr *) &server_addr, addr_len) < 0) { + perror("Failed to bind TCP socket"); + exit(errno); + } + char buffer[BUF_SIZE]; + printf("Searching server... "); + // send broadcast with the token + if (sendto(udp_sock, token, strlen(token), 0, (struct sockaddr *) &broadcast_addr, sizeof(broadcast_addr)) < strlen(token)) { + perror("Failed to broadcast request"); + exit(errno); + } + // receive response from server + if (recvfrom(udp_sock, (void *) buffer, 2, 0, (struct sockaddr *) &server_addr, &addr_len) < 2) { + perror("Failed to receive response"); + exit(errno); + } + // check server response + printf("Server found, response: %.2s\n", buffer); + if (memcmp(buffer, "OK", 2) != 0) { + if (memcmp(buffer, "NO", 2) != 0) { + printf("Error: Got unexpected response from server\n"); + } else { + printf("Request was rejected by server\n"); + } + exit(1); + } + // connect to server + server_addr.sin_port = htons(PORT_TCP); + if (connect(tcp_sock, (struct sockaddr *) &server_addr, addr_len) < 0) { + perror("Failed to connect to server"); + exit(errno); + } + printf("Getting file...\n"); + // get file name + ssize_t rcount; + if ((rcount = read(tcp_sock, buffer, BUF_SIZE)) <= 0) { + perror("Failed to get file name"); + exit(errno); + } + fname_len = strlen(buffer) + 1; + strcpy(fname, buffer); + // if path is a directory, append file name to it + if (stat(abspath, &stat_buf) == 0 && S_ISDIR(stat_buf.st_mode)) { + abspath[abspath_len - 1] = '/'; + abspath[abspath_len] = 0; + strcat(abspath, fname); + } + FILE *file = fopen(abspath, "wb"); + if (file == NULL) { + perror("Failed to open file"); + exit(errno); + } + if (rcount > fname_len) { + fwrite(buffer + fname_len, 1, rcount - fname_len, file); + } + // get file data + for (;;) { + rcount = read(tcp_sock, buffer, BUF_SIZE); + if (rcount == 0) { + break; + } + if (rcount < 0) { + perror("Failed to get file"); + exit(errno); + } + fwrite(buffer, 1, rcount, file); + } + fclose(file); + printf("Success!\nFile path: %s\n", realpath(abspath, abspath)); +} + +int main(int argc, char **argv) { + if (argc == 1) { + printf("Use '-h' to view help.\n"); + return 0; + } + char *path = NULL, + *token = NULL; + int mode_share; + int opt; + while ((opt = getopt(argc, argv, "hs:g:o:V")) != -1) { + switch (opt) { + case 'h': + printf( + "(C) Masahiko AMANO aka H1K0, 2024—present\n" + "(https://github.com/H1K0/aiken)\n\n" + "Usage:\n" + " aiken [arguments]\n\n" + "Options:\n" + " -h Print this help and exit\n" + " -s Share the file of the specified \n" + " -g Get the file by its token\n" + " -o Specify output path when getting file\n" + " (defaults to current working directory and\n" + " the initial file name)\n" + " -V Print version info and exit\n" + ); + return 0; + case 'V': + printf("Aiken 0.1\n"); + return 0; + case 's': + mode_share = 1; + path = realpath(optarg, path); + if (path == NULL) { + perror("Invalid share path"); + return errno; + } + break; + case 'g': + mode_share = 0; + token = optarg; + break; + case 'o': + if (mode_share) { + fprintf(stderr, "Warning: The option '-o' is ignored when sharing.\n"); + break; + } + path = optarg; + break; + } + } + if (mode_share) { + share(path); + } else { + get(token, path); + } + return 0; +}