update stuffs
This commit is contained in:
3
Makefile
3
Makefile
@@ -1,6 +1,9 @@
|
||||
chatserver: chatserver.c
|
||||
cc chatserver.c -o chatserver -O3
|
||||
|
||||
#chatclient: chatclient.c
|
||||
# gcc -o chatclient chatclient.c `pkg-config --cflags --libs gtk+-3.0` -lpthread
|
||||
|
||||
chatclient-cli: chatclient-cli.c
|
||||
cc chatclient-cli.c -o chatclient-cli -lpthread -lncurses
|
||||
|
||||
|
||||
307
chatclient-cli.c
307
chatclient-cli.c
@@ -1,30 +1,33 @@
|
||||
#include "shared.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
#include <locale.h>
|
||||
|
||||
#include <ncurses.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <netdb.h>
|
||||
#include <pthread.h>
|
||||
|
||||
struct {
|
||||
bool title_header;
|
||||
bool message_field;
|
||||
bool input_field;
|
||||
struct {
|
||||
bool title_header;
|
||||
bool message_field;
|
||||
bool input_field;
|
||||
} needs_redraw;
|
||||
struct { int x, y; } cursor;
|
||||
int scroll_offset;
|
||||
int unread;
|
||||
} render_state = {0};
|
||||
} render_state = {
|
||||
.needs_redraw = {1, 1, 1},
|
||||
.cursor = {0, 0},
|
||||
.scroll_offset = 0,
|
||||
.unread = 0
|
||||
};
|
||||
|
||||
struct input_state_t {
|
||||
char buffer[1024];
|
||||
char buffer[MAX_MESSAGE_LENGTH];
|
||||
int buffer_size;
|
||||
int content_length;
|
||||
int cursor_pos;
|
||||
@@ -35,7 +38,7 @@ int server_fd;
|
||||
FILE *reader;
|
||||
FILE *writer;
|
||||
|
||||
struct input_state_t input_state = (struct input_state_t){{0}, sizeof(input_state.buffer) - 1, 0, 0, 0};
|
||||
struct input_state_t input_state = (struct input_state_t){{0}, MAX_MESSAGE_LENGTH, 0, 0, 0};
|
||||
|
||||
static int input_state_insert_char(const char ch) {
|
||||
if (input_state.content_length >= input_state.buffer_size - 1) return 1;
|
||||
@@ -67,8 +70,8 @@ static void input_state_backspace_char() {
|
||||
}
|
||||
}
|
||||
|
||||
static int input_state_handle_input_line(const int ch) {
|
||||
render_state.input_field = 0;
|
||||
static int input_state_handle_input(const int ch) {
|
||||
render_state.needs_redraw.input_field = 1;
|
||||
if (ch == '\n' || ch == '\r' || ch == KEY_ENTER) return 1;
|
||||
if (ch == KEY_BACKSPACE || ch == 127) { input_state_backspace_char(); return 0; }
|
||||
if (ch == KEY_DC) { input_state_delete_char(); return 0; }
|
||||
@@ -78,28 +81,30 @@ static int input_state_handle_input_line(const int ch) {
|
||||
if (ch == KEY_END) { input_state.cursor_pos = input_state.content_length; return 0; }
|
||||
if (ch == KEY_PPAGE) {
|
||||
render_state.scroll_offset += LINES - 3 - 1 -2;
|
||||
render_state.message_field = false;
|
||||
render_state.title_header = false;
|
||||
render_state.needs_redraw.message_field = 1;
|
||||
render_state.unread = 0;
|
||||
render_state.needs_redraw.title_header = 1;
|
||||
return 0;
|
||||
}
|
||||
if (ch == KEY_NPAGE) {
|
||||
render_state.scroll_offset -= LINES - 3 - 1 -2;
|
||||
if (render_state.scroll_offset < 0) render_state.scroll_offset = 0;
|
||||
render_state.message_field = false;
|
||||
render_state.title_header = false;
|
||||
render_state.needs_redraw.message_field = 1;
|
||||
render_state.needs_redraw.title_header = 1;
|
||||
return 0;
|
||||
}
|
||||
if (ch == KEY_UP) {
|
||||
render_state.scroll_offset++;
|
||||
render_state.message_field = false;
|
||||
render_state.title_header = false;
|
||||
render_state.needs_redraw.message_field = 1;
|
||||
render_state.needs_redraw.title_header = 1;
|
||||
return 0;
|
||||
}
|
||||
if (ch == KEY_DOWN) {
|
||||
if (render_state.scroll_offset > 0) {
|
||||
render_state.scroll_offset--;
|
||||
render_state.message_field = false;
|
||||
render_state.title_header = false;
|
||||
render_state.needs_redraw.message_field = 1;
|
||||
if (render_state.scroll_offset == 0) render_state.unread = 0;
|
||||
render_state.needs_redraw.title_header = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -132,9 +137,9 @@ static void add_new_message(const char* str) {
|
||||
if (render_state.scroll_offset > 0) {
|
||||
render_state.scroll_offset++;
|
||||
render_state.unread++;
|
||||
render_state.title_header = 0;
|
||||
render_state.needs_redraw.title_header = 1;
|
||||
}
|
||||
render_state.message_field = 0;
|
||||
render_state.needs_redraw.message_field = 1;
|
||||
pthread_mutex_unlock(&messages.lock);
|
||||
}
|
||||
|
||||
@@ -142,47 +147,131 @@ static void add_new_message(const char* str) {
|
||||
#define PROGRAM_NAME "chatclient-cli"
|
||||
|
||||
#define CC_COLOR_PAIR_TITLE_HEADER 1
|
||||
#define CC_COLOR_PAIR_TITLE_HEADER2 3
|
||||
#define CC_COLOR_PAIR_INPUT 2
|
||||
|
||||
char server_str[CC_ADDRLEN];
|
||||
int server_str_len;
|
||||
|
||||
static void cc_render_title_header(
|
||||
const int x,
|
||||
const int y,
|
||||
const int w
|
||||
) {
|
||||
char system_time[20];
|
||||
const int time_length = strftime(system_time, 20, "%Y-%m-%d %H:%M:%S", localtime(&(time_t){time(NULL)}));
|
||||
char system_time[21];
|
||||
const time_t now = time(NULL);
|
||||
int time_length = strftime(system_time, sizeof(system_time), "%Y-%m-%d %H:%M:%S", localtime(&now));
|
||||
|
||||
char indicator[32] = "";
|
||||
char indicator[32] = {0};
|
||||
int indicator_length = 0;
|
||||
if (render_state.unread > 0) {
|
||||
indicator_length = snprintf(indicator, sizeof(indicator), " [%d unread]", render_state.unread);
|
||||
indicator_length = snprintf(indicator, sizeof(indicator), " %c%d unread%c",
|
||||
now % 2 ? ' ' : '[',
|
||||
render_state.unread,
|
||||
now % 2 ? ' ' : ']'
|
||||
);
|
||||
}
|
||||
|
||||
move(y, x);
|
||||
attron(COLOR_PAIR(CC_COLOR_PAIR_TITLE_HEADER));
|
||||
const int name_length = strlen(PROGRAM_NAME);
|
||||
const int total_length = name_length + indicator_length + server_str_len + time_length;
|
||||
char header_str[1024];
|
||||
int n;
|
||||
|
||||
const int name_length = sizeof(PROGRAM_NAME) - 1;
|
||||
const int total_length = name_length + time_length + indicator_length;
|
||||
int left_right_padding = w / 16;
|
||||
if (left_right_padding > 2) left_right_padding = 2;
|
||||
if (total_length + left_right_padding * 2 + 2 * 2 <= w) {
|
||||
const int total_padding = w - left_right_padding * 2 - total_length;
|
||||
const int padding2 = total_padding / 2;
|
||||
const int padding1 = total_padding - padding2;
|
||||
|
||||
if (total_length + 2 > w) {
|
||||
const int padding = (w - name_length - indicator_length) / 2;
|
||||
PRINT_SPACES(padding);
|
||||
printw("%-*s", w - padding, PROGRAM_NAME);
|
||||
printw("%s", indicator);
|
||||
n = snprintf(header_str, sizeof(header_str),
|
||||
"%*c"
|
||||
"%s"
|
||||
"%s"
|
||||
"%*c"
|
||||
"%s"
|
||||
"%*c"
|
||||
"%s"
|
||||
"%*c",
|
||||
left_right_padding, ' ',
|
||||
PROGRAM_NAME,
|
||||
indicator,
|
||||
padding1, ' ',
|
||||
server_str,
|
||||
padding2, ' ',
|
||||
system_time,
|
||||
left_right_padding, ' '
|
||||
);
|
||||
}
|
||||
else if (name_length + indicator_length + server_str_len + left_right_padding * 2 + 2 <= w) {
|
||||
const int padding = w - left_right_padding * 2 - name_length - indicator_length - server_str_len;
|
||||
|
||||
n = snprintf(header_str, sizeof(header_str),
|
||||
"%*c"
|
||||
"%s"
|
||||
"%s"
|
||||
"%*c"
|
||||
"%s"
|
||||
"%*c",
|
||||
left_right_padding, ' ',
|
||||
PROGRAM_NAME,
|
||||
indicator,
|
||||
padding, ' ',
|
||||
server_str,
|
||||
left_right_padding, ' '
|
||||
);
|
||||
}
|
||||
else if (name_length + indicator_length <= w) {
|
||||
const int total_left_right_padding = w - name_length - indicator_length;
|
||||
int right_padding = total_left_right_padding / 2;
|
||||
int left_padding = total_left_right_padding - right_padding;
|
||||
|
||||
n = snprintf(header_str, sizeof(header_str),
|
||||
"%*c"
|
||||
"%s"
|
||||
"%s"
|
||||
"%*c",
|
||||
left_padding, ' ',
|
||||
PROGRAM_NAME,
|
||||
indicator,
|
||||
right_padding, ' '
|
||||
);
|
||||
}
|
||||
else if (name_length <= w) {
|
||||
const int total_left_right_padding = w - name_length;
|
||||
int right_padding = total_left_right_padding / 2;
|
||||
int left_padding = total_left_right_padding - right_padding;
|
||||
|
||||
n = snprintf(header_str, sizeof(header_str),
|
||||
"%*c"
|
||||
"%s"
|
||||
"%*c",
|
||||
left_padding, ' ',
|
||||
PROGRAM_NAME,
|
||||
right_padding, ' '
|
||||
);
|
||||
}
|
||||
else {
|
||||
const int left_right_padding = 2;
|
||||
const int padding = w - left_right_padding * 2 - total_length;
|
||||
|
||||
PRINT_SPACES(left_right_padding);
|
||||
printw("%s", PROGRAM_NAME);
|
||||
printw("%s", indicator);
|
||||
PRINT_SPACES(padding);
|
||||
printw("%s", system_time);
|
||||
PRINT_SPACES(left_right_padding);
|
||||
n = snprintf(header_str, sizeof(header_str),
|
||||
"%*c",
|
||||
w, ' '
|
||||
);
|
||||
}
|
||||
|
||||
attroff(COLOR_PAIR(CC_COLOR_PAIR_TITLE_HEADER));
|
||||
int begin_color = render_state.scroll_offset % (w * 2) > w ? CC_COLOR_PAIR_TITLE_HEADER : CC_COLOR_PAIR_TITLE_HEADER2;
|
||||
int end_color = render_state.scroll_offset % (w * 2) < w ? CC_COLOR_PAIR_TITLE_HEADER : CC_COLOR_PAIR_TITLE_HEADER2;
|
||||
int current_color = begin_color;
|
||||
move(y, x);
|
||||
attron(COLOR_PAIR(current_color));
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (i == render_state.scroll_offset % w) {
|
||||
attroff(COLOR_PAIR(current_color));
|
||||
current_color = end_color;
|
||||
attron(COLOR_PAIR(current_color));
|
||||
}
|
||||
addch(header_str[i]);
|
||||
}
|
||||
attroff(COLOR_PAIR(current_color));
|
||||
}
|
||||
|
||||
static void cc_render_message_field(
|
||||
@@ -270,22 +359,22 @@ static void cc_render_ui(
|
||||
pthread_mutex_lock(&messages.lock);
|
||||
bool do_refresh = false;
|
||||
|
||||
if (!render_state.title_header) {
|
||||
render_state.title_header = true;
|
||||
if (render_state.needs_redraw.title_header) {
|
||||
render_state.needs_redraw.title_header = 0;
|
||||
cc_render_title_header(x, y, w);
|
||||
do_refresh = true;
|
||||
do_refresh = 1;
|
||||
}
|
||||
|
||||
if (!render_state.message_field) {
|
||||
render_state.message_field = true;
|
||||
if (render_state.needs_redraw.message_field) {
|
||||
render_state.needs_redraw.message_field = 0;
|
||||
cc_render_message_field(x, y + 1, w, h - 3);
|
||||
do_refresh = true;
|
||||
do_refresh = 1;
|
||||
}
|
||||
|
||||
if (!render_state.input_field) {
|
||||
render_state.input_field = true;
|
||||
if (render_state.needs_redraw.input_field) {
|
||||
render_state.needs_redraw.input_field = 0;
|
||||
cc_render_input_field(x, y + h - 3, w);
|
||||
do_refresh = true;
|
||||
do_refresh = 1;
|
||||
}
|
||||
|
||||
if (do_refresh) {
|
||||
@@ -300,9 +389,9 @@ static void update_terminal_size() {
|
||||
refresh();
|
||||
clear();
|
||||
initscr();
|
||||
render_state.title_header = false;
|
||||
render_state.message_field = false;
|
||||
render_state.input_field = false;
|
||||
render_state.needs_redraw.title_header = 1;
|
||||
render_state.needs_redraw.message_field = 1;
|
||||
render_state.needs_redraw.input_field = 1;
|
||||
}
|
||||
|
||||
static int handle_input() {
|
||||
@@ -320,40 +409,6 @@ static int handle_input() {
|
||||
return ch;
|
||||
}
|
||||
|
||||
#define exit_error(...) do { \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
endwin(); exit(EXIT_FAILURE); \
|
||||
} while (0)
|
||||
|
||||
#define exit_perror(s) do { \
|
||||
perror(s); \
|
||||
endwin(); exit(EXIT_FAILURE); \
|
||||
} while (0)
|
||||
|
||||
static int connect_to_server(struct in6_addr addr, int port) {
|
||||
struct sockaddr_in6 server_addr;
|
||||
|
||||
server_fd = socket(AF_INET6, SOCK_STREAM, 0);
|
||||
if (server_fd < 0) exit_perror("Socket creation failed");
|
||||
const int server_fd2 = dup(server_fd);
|
||||
if (server_fd2 < 0) exit_perror("dup");
|
||||
|
||||
server_addr.sin6_family = AF_INET6;
|
||||
server_addr.sin6_addr = addr;
|
||||
server_addr.sin6_port = htons(port);
|
||||
|
||||
int opt = 0;
|
||||
if (setsockopt(server_fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) < 0) {
|
||||
// i guess so bro
|
||||
}
|
||||
|
||||
if (connect(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) exit_perror("connect");
|
||||
|
||||
if ((reader = fdopen(server_fd, "r")) == NULL) exit_perror("fdopen reader");
|
||||
if ((writer = fdopen(server_fd2, "w")) == NULL) exit_perror("fdopen writer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *reader_thread(void *data) {
|
||||
char buffer[1078];
|
||||
while (fgets(buffer, sizeof(buffer), reader) != NULL) {
|
||||
@@ -364,74 +419,24 @@ void *reader_thread(void *data) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int resolve_domain_to_ipv6(const char *hostname, struct in6_addr *server_addr) {
|
||||
struct addrinfo hints, *res;
|
||||
int found = 0;
|
||||
|
||||
memset(&hints, 0, sizeof hints);
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
if (getaddrinfo(hostname, NULL, &hints, &res) != 0) return 0;
|
||||
|
||||
for (struct addrinfo *p = res; p != NULL; p = p->ai_next) {
|
||||
if (p->ai_family == AF_INET6) {
|
||||
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
|
||||
memcpy(server_addr, &ipv6->sin6_addr, sizeof(struct in6_addr));
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
if (p->ai_family == AF_INET && !found) {
|
||||
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
|
||||
memset(server_addr, 0, sizeof(struct in6_addr));
|
||||
server_addr->s6_addr[10] = 0xff;
|
||||
server_addr->s6_addr[11] = 0xff;
|
||||
memcpy(&server_addr->s6_addr[12], &(ipv4->sin_addr), 4);
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
return found;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
struct in6_addr server_addr = in6addr_any;
|
||||
int port = 5000;
|
||||
if (argc >= 2) {
|
||||
if (inet_pton(AF_INET6, argv[1], &server_addr) != 1) {
|
||||
struct in_addr v4;
|
||||
if (inet_pton(AF_INET, argv[1], &v4) != 1) {
|
||||
if (!resolve_domain_to_ipv6(argv[1], &server_addr)) {
|
||||
exit_error("invalid address or domain: %s\n", argv[1]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
memset(&server_addr, 0, sizeof(server_addr));
|
||||
server_addr.s6_addr[10] = 0xff;
|
||||
server_addr.s6_addr[11] = 0xff;
|
||||
memcpy(&server_addr.s6_addr[12], &v4, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (argc >= 3) {
|
||||
port = atoi(argv[2]);
|
||||
if (port <= 0) exit_error("unknown argument: %s\n", argv[2]);
|
||||
}
|
||||
setlocale(LC_ALL, "");
|
||||
initscr();
|
||||
start_color();
|
||||
use_default_colors();
|
||||
init_pair(CC_COLOR_PAIR_TITLE_HEADER, COLOR_BLACK, COLOR_CYAN);
|
||||
init_pair(CC_COLOR_PAIR_TITLE_HEADER2, COLOR_BLACK, COLOR_YELLOW);
|
||||
init_pair(CC_COLOR_PAIR_INPUT, COLOR_GREEN, -1);
|
||||
cbreak();
|
||||
noecho();
|
||||
curs_set(1);
|
||||
keypad(stdscr, TRUE);
|
||||
|
||||
struct sockaddr_in6 server_addr;
|
||||
cc_client_parse_args_and_connect(argc, argv, &server_addr, &server_fd, &reader, &writer);
|
||||
server_str_len = sockaddr2str((struct sockaddr*)&server_addr, server_str, sizeof(server_str));
|
||||
init_messages();
|
||||
|
||||
connect_to_server(server_addr, port);
|
||||
pthread_t reader_thread_id;
|
||||
pthread_create(&reader_thread_id, NULL, reader_thread, NULL);
|
||||
|
||||
@@ -442,20 +447,20 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
const int input = handle_input();
|
||||
|
||||
if (input_state_handle_input_line(input)) {
|
||||
if (input_state_handle_input(input)) {
|
||||
if (input_state.content_length > 0) {
|
||||
fprintf(writer, "%s\n", input_state.buffer);
|
||||
fflush(writer);
|
||||
input_state = (struct input_state_t){{0}, sizeof(input_state.buffer) - 1, 0, 0, 0};
|
||||
render_state.message_field = false;
|
||||
render_state.unread = 0;
|
||||
input_state = (struct input_state_t){{0}, MAX_MESSAGE_LENGTH, 0, 0, 0};
|
||||
render_state.needs_redraw.message_field = 1;
|
||||
render_state.unread = -1;
|
||||
}
|
||||
}
|
||||
|
||||
const time_t now = time(NULL);
|
||||
if (now > last_second) {
|
||||
last_second = now;
|
||||
render_state.title_header = false;
|
||||
render_state.needs_redraw.title_header = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
71
chatserver.c
71
chatserver.c
@@ -7,26 +7,7 @@
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
static void sockaddr2str(const struct sockaddr *sa, char *s, const size_t maxlen) {
|
||||
if (sa->sa_family == AF_INET) {
|
||||
const struct sockaddr_in *sin = (struct sockaddr_in *)sa;
|
||||
inet_ntop(AF_INET, &sin->sin_addr, s, maxlen);
|
||||
const size_t len = strnlen(s, maxlen);
|
||||
snprintf(s + len, maxlen - len, ":%u", ntohs(sin->sin_port));
|
||||
return;
|
||||
}
|
||||
|
||||
if (sa->sa_family == AF_INET6) {
|
||||
const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
|
||||
s[0] = '[';
|
||||
inet_ntop(AF_INET6, &sin6->sin6_addr, s + 1, maxlen - 1);
|
||||
const size_t len = strnlen(s, maxlen);
|
||||
snprintf(s + len, maxlen - len, "]:%u", ntohs(sin6->sin6_port));
|
||||
return;
|
||||
}
|
||||
|
||||
strncpy(s, "unknown", maxlen);
|
||||
}
|
||||
#include "shared.h"
|
||||
|
||||
#define MAX_CLIENTS 100
|
||||
#define BUFFER_SIZE 1024
|
||||
@@ -56,6 +37,29 @@ typedef struct {
|
||||
fflush(stdout); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
const char* ansi_colors[] = {
|
||||
"\033[30m", "\033[31m", "\033[32m", "\033[33m", "\033[34m", "\033[35m", "\033[36m", "\033[37m",
|
||||
"\033[90m", "\033[91m", "\033[92m", "\033[93m", "\033[94m", "\033[95m", "\033[96m", "\033[97m",
|
||||
"\033[0m"
|
||||
};
|
||||
|
||||
static unsigned long djb2(const unsigned char *str, size_t size) {
|
||||
unsigned long hash = 5381;
|
||||
while (size--) hash = ((hash << 5) + hash) + *str++;
|
||||
return hash;
|
||||
}
|
||||
|
||||
const char* sockaddr_color(const struct sockaddr *addr) {
|
||||
if (addr->sa_family == AF_INET) {
|
||||
return ansi_colors[djb2((const unsigned char*)&((const struct sockaddr_in*)&addr)->sin_addr, 4) % 16];
|
||||
}
|
||||
if (addr->sa_family == AF_INET6) {
|
||||
return ansi_colors[djb2((const unsigned char*)&((const struct sockaddr_in6*)&addr)->sin6_addr, 16) % 16];
|
||||
}
|
||||
return ansi_colors[16];
|
||||
}*/
|
||||
|
||||
void* handle_client(void* _arg) {
|
||||
const pthread_input_t* arg = _arg;
|
||||
const int client_fd = arg->sock;
|
||||
@@ -63,20 +67,20 @@ void* handle_client(void* _arg) {
|
||||
free(_arg);
|
||||
|
||||
char message[BUFFER_SIZE];
|
||||
|
||||
|
||||
FILE* reader = fdopen(client_fd, "r");
|
||||
|
||||
|
||||
if (reader == NULL) {
|
||||
perror("fdopen");
|
||||
close(client_fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
while (fgets(message, BUFFER_SIZE, reader) != NULL) {
|
||||
message[strcspn(message, "\n")] = 0;
|
||||
if (message[0] == '\0') continue;
|
||||
|
||||
char client_ip[INET6_ADDRSTRLEN + 8];
|
||||
|
||||
char client_ip[CC_ADDRLEN];
|
||||
sockaddr2str((struct sockaddr*)&addr, client_ip, sizeof(client_ip));
|
||||
tell_everyone("%s: %s\n", client_ip, message);
|
||||
}
|
||||
@@ -89,23 +93,12 @@ void* handle_client(void* _arg) {
|
||||
break;
|
||||
}
|
||||
pthread_mutex_unlock(&clients.lock);
|
||||
char client_ip[INET6_ADDRSTRLEN + 8];
|
||||
char client_ip[CC_ADDRLEN];
|
||||
sockaddr2str((struct sockaddr*)&addr, client_ip, sizeof(client_ip));
|
||||
tell_everyone("- %s. total clients: %d\n", client_ip, clients.count);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define streq(a, b) (strcmp((a), (b)) == 0)
|
||||
#define exit_error(...) do { \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
exit(EXIT_FAILURE); \
|
||||
} while (0)
|
||||
|
||||
#define exit_perror(s) do { \
|
||||
perror(s); \
|
||||
exit(EXIT_FAILURE); \
|
||||
} while (0)
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
struct in6_addr bind_addr = in6addr_any;
|
||||
int port = 5000;
|
||||
@@ -165,8 +158,8 @@ int main(int argc, char* argv[]) {
|
||||
pthread_create(&thread, NULL, handle_client, pi);
|
||||
pthread_detach(thread);
|
||||
pthread_mutex_unlock(&clients.lock);
|
||||
char ip_buf[INET6_ADDRSTRLEN + 8];
|
||||
char ip_buf[CC_ADDRLEN];
|
||||
sockaddr2str((struct sockaddr*)&client_addr, ip_buf, sizeof(ip_buf));
|
||||
tell_everyone("+ %s. total clients: %d\n", ip_buf, clients.count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
120
shared.h
Normal file
120
shared.h
Normal file
@@ -0,0 +1,120 @@
|
||||
#pragma once
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#define CC_ADDRLEN (INET6_ADDRSTRLEN + 8) // [...:12345]
|
||||
|
||||
static int sockaddr2str(const struct sockaddr *sa, char *s, const size_t maxlen) {
|
||||
if (sa->sa_family == AF_INET) {
|
||||
const struct sockaddr_in *sin = (struct sockaddr_in *)sa;
|
||||
inet_ntop(AF_INET, &sin->sin_addr, s, maxlen);
|
||||
const size_t len = strnlen(s, maxlen);
|
||||
return len + snprintf(s + len, maxlen - len, ":%u", ntohs(sin->sin_port));
|
||||
}
|
||||
|
||||
if (sa->sa_family == AF_INET6) {
|
||||
const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
|
||||
s[0] = '[';
|
||||
inet_ntop(AF_INET6, &sin6->sin6_addr, s + 1, maxlen - 1);
|
||||
const size_t len = strnlen(s, maxlen);
|
||||
return len + snprintf(s + len, maxlen - len, "]:%u", ntohs(sin6->sin6_port));
|
||||
}
|
||||
|
||||
strncpy(s, "unknown", maxlen);
|
||||
return 7;
|
||||
}
|
||||
|
||||
#define MAX_MESSAGE_LENGTH 1024
|
||||
|
||||
#define streq(a, b) (strcmp((a), (b)) == 0)
|
||||
|
||||
#define exit_error(...) do { \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
exit(EXIT_FAILURE); \
|
||||
} while (0)
|
||||
|
||||
#define exit_perror(s) do { \
|
||||
perror(s); \
|
||||
exit(EXIT_FAILURE); \
|
||||
} while (0)
|
||||
|
||||
static int resolve_domain_to_ipv6(const char *hostname, struct in6_addr *server_addr) {
|
||||
struct addrinfo hints, *res;
|
||||
int found = 0;
|
||||
|
||||
memset(&hints, 0, sizeof hints);
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
if (getaddrinfo(hostname, NULL, &hints, &res) != 0) return 0;
|
||||
|
||||
for (struct addrinfo *p = res; p != NULL; p = p->ai_next) {
|
||||
if (p->ai_family == AF_INET6) {
|
||||
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
|
||||
memcpy(server_addr, &ipv6->sin6_addr, sizeof(struct in6_addr));
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
if (p->ai_family == AF_INET && !found) {
|
||||
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
|
||||
memset(server_addr, 0, sizeof(struct in6_addr));
|
||||
server_addr->s6_addr[10] = 0xff;
|
||||
server_addr->s6_addr[11] = 0xff;
|
||||
memcpy(&server_addr->s6_addr[12], &(ipv4->sin_addr), 4);
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
return found;
|
||||
}
|
||||
|
||||
static int cc_client_parse_args_and_connect(int argc, char* argv[], struct sockaddr_in6* server_addr, int* server_fd, FILE** reader, FILE** writer) {
|
||||
struct in6_addr bind_addr = in6addr_any;
|
||||
int port = 5000;
|
||||
if (argc >= 2) {
|
||||
if (inet_pton(AF_INET6, argv[1], &bind_addr) != 1) {
|
||||
struct in_addr v4;
|
||||
if (inet_pton(AF_INET, argv[1], &v4) != 1) {
|
||||
if (!resolve_domain_to_ipv6(argv[1], &bind_addr)) {
|
||||
exit_error("invalid address or domain: %s\n", argv[1]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
memset(&bind_addr, 0, sizeof(bind_addr));
|
||||
bind_addr.s6_addr[10] = 0xff;
|
||||
bind_addr.s6_addr[11] = 0xff;
|
||||
memcpy(&bind_addr.s6_addr[12], &v4, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (argc >= 3) {
|
||||
port = atoi(argv[2]);
|
||||
if (port <= 0) exit_error("unknown argument: %s\n", argv[2]);
|
||||
}
|
||||
|
||||
*server_fd = socket(AF_INET6, SOCK_STREAM, 0);
|
||||
if (*server_fd < 0) exit_perror("socket");
|
||||
const int server_fd2 = dup(*server_fd);
|
||||
if (server_fd2 < 0) exit_perror("dup");
|
||||
|
||||
server_addr->sin6_family = AF_INET6;
|
||||
server_addr->sin6_addr = bind_addr;
|
||||
server_addr->sin6_port = htons(port);
|
||||
|
||||
int opt = 0;
|
||||
if (setsockopt(*server_fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) < 0) {
|
||||
// i guess so bro
|
||||
}
|
||||
|
||||
if (connect(*server_fd, (struct sockaddr *)server_addr, sizeof(*server_addr)) < 0) exit_perror("connect");
|
||||
|
||||
if ((*reader = fdopen(*server_fd, "r")) == NULL) exit_perror("fdopen reader");
|
||||
if ((*writer = fdopen(server_fd2, "w")) == NULL) exit_perror("fdopen writer");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user