From a4978e77a34e7461702fb617c335ddb0ab241e05 Mon Sep 17 00:00:00 2001 From: Evan Lloyd New-Schmidt Date: Sun, 2 Aug 2020 11:39:07 -0700 Subject: [PATCH 1/4] Add version negotiation to network protocol Before making more breaking changes to the networking protocol (like sending user coordinates back and forth for cursor support), it seemed best to add a method of negotiating a protocol version so things can fail or downgrade more gracefully. As implemented here, previous versions of servers and clients will not work with these updated versions. This would be a pretty big problem, except that as far as I know nobody uses this project regularly, especially in a networked/remote capacity. Because previously the client expected canvas data to be sent over immediately, the only way that I can think of to support older clients while not requiring that canvas send is to add a timeout on the server that waits for a version number and then sends the canvas if nothing is sent, which seems pretty unideal. --- src/network.c | 20 ++++++++++++++++++++ src/server.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/network.c b/src/network.c index 154da44..4c2b6c9 100644 --- a/src/network.c +++ b/src/network.c @@ -29,6 +29,8 @@ struct hostent *hostinfo; struct sockaddr_in address; struct addrinfo hints, *servinfo; +const char* PROTOCOL_VERSION = "1.0"; + /* Connects to server and returns its canvas * */ @@ -61,6 +63,24 @@ Canvas *net_init(char *in_hostname, char *in_port) { sockstream = fdopen(sockfd, "r+"); + // "negotiate" protocol version + char version_request_msg[16]; + snprintf(version_request_msg, 16, "v %s\n", PROTOCOL_VERSION); + if (write(sockfd, version_request_msg, strlen(version_request_msg)) < 0) { + eprintf("version negotiation: write error\n"); + exit(1); + } + + if (getline(&msg_buf, &msg_size, sockstream) == -1) { + eprintf("version negotiation: read error: the cow says 'moo'\n"); + exit(1); + } + if (!(msg_buf[0] == 'v' && msg_buf[1] == 'o' && msg_buf[2] == 'k')) { + eprintf("Failed to negotiate protocol version: the server says '%s'\n", msg_buf); + exit(1); + } + + // receive canvas from server getline(&msg_buf, &msg_size, sockstream); char *command = strtok(msg_buf, " "); if (!strcmp(command, "cs")) { diff --git a/src/server.c b/src/server.c index ce10c42..fd8ba5c 100644 --- a/src/server.c +++ b/src/server.c @@ -25,6 +25,8 @@ static _Atomic unsigned int cli_count = 0; static int uid = 10; +const char* PROTOCOL_VERSION = "1.0"; + #define MAX_CLIENTS 100 #define BUFFER_SZ 2048 @@ -154,6 +156,35 @@ void *handle_client(void *arg) { print_client_addr(cli->addr); printf(" referenced by %d\n", cli->uid); + // protocol negotiation + if ((rlen = read(cli->connfd, buff_in, sizeof(buff_in) - 1)) < 0) { + printf("version negotation: error reading from socket\n"); + goto CLIENT_CLOSE; + } + buff_in[rlen] = '\0'; + strip_newline(buff_in); + char* cmd = strtok(buff_in, " "); + if (cmd == NULL || cmd[0] != 'v') { + printf("version negotiation: client command not 'v'\n"); + + goto CLIENT_CLOSE; + } + char* client_version = strtok(NULL, " "); + if (client_version == NULL) { + printf("version negotiation: unable to parse client version\n"); + send_message_self("can't parse version\n", cli->connfd); + goto CLIENT_CLOSE; + } + if (strcmp(client_version, PROTOCOL_VERSION) != 0) { + printf("version negotiation: unknown client protocol version: '%s'\n", client_version); + send_message_self("unknown protocol - supported protocol versions: ", cli->connfd); + send_message_self(PROTOCOL_VERSION, cli->connfd); + send_message_self("\n", cli->connfd); + goto CLIENT_CLOSE; + } + send_message_self("vok\n", cli->connfd); + + // send canvas sprintf(buff_out, "cs %d %d\n", canvas->num_rows, canvas->num_cols); send_message_self(buff_out, cli->connfd); printf("sent canvas size\n"); @@ -199,6 +230,7 @@ void *handle_client(void *arg) { send_message_self(canvas_buf, cli->connfd); } } + CLIENT_CLOSE: /* Close connection */ close(cli->connfd); From 95c24c15a4b856366b6516d9d4889a901eef7ad5 Mon Sep 17 00:00:00 2001 From: Evan Lloyd New-Schmidt Date: Sun, 9 Aug 2020 17:01:51 -0700 Subject: [PATCH 2/4] Add errno messages for protocol negotiation --- src/network.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/network.c b/src/network.c index 4c2b6c9..abfa1fb 100644 --- a/src/network.c +++ b/src/network.c @@ -29,7 +29,7 @@ struct hostent *hostinfo; struct sockaddr_in address; struct addrinfo hints, *servinfo; -const char* PROTOCOL_VERSION = "1.0"; +const char *PROTOCOL_VERSION = "1.0"; /* Connects to server and returns its canvas * @@ -67,16 +67,17 @@ Canvas *net_init(char *in_hostname, char *in_port) { char version_request_msg[16]; snprintf(version_request_msg, 16, "v %s\n", PROTOCOL_VERSION); if (write(sockfd, version_request_msg, strlen(version_request_msg)) < 0) { - eprintf("version negotiation: write error\n"); + perror("version negotiation: write error"); exit(1); } if (getline(&msg_buf, &msg_size, sockstream) == -1) { - eprintf("version negotiation: read error: the cow says 'moo'\n"); + perror("version negotiation: read error"); exit(1); } if (!(msg_buf[0] == 'v' && msg_buf[1] == 'o' && msg_buf[2] == 'k')) { - eprintf("Failed to negotiate protocol version: the server says '%s'\n", msg_buf); + eprintf("Failed to negotiate protocol version: the server says '%s'\n", + msg_buf); exit(1); } From 1e4c0df9f467504f837ac6c522dfc69f3f2bf1c1 Mon Sep 17 00:00:00 2001 From: Evan Lloyd New-Schmidt Date: Sat, 29 Aug 2020 20:24:16 -0700 Subject: [PATCH 3/4] Switch to less-common default port Bonus: if you squint really hard it looks like ASCII --- src/frontend.c | 2 +- src/network.c | 2 +- src/server.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/frontend.c b/src/frontend.c index 52b5bfb..1205650 100644 --- a/src/frontend.c +++ b/src/frontend.c @@ -173,7 +173,7 @@ void parse_args(int argc, char *argv[], arguments_t *arguments) { server = arg_strn("s", "server", "", 0, 1, "address of server to connect to"), port = arg_strn("p", "port", "", 0, 1, - "port of server to connect to (default 5000)"), + "port of server to connect to (default 45011)"), file = arg_filen(NULL, NULL, "[FILE]", 0, 1, "filepath for read/write ('-' to read from stdin)"), end = arg_end(20), diff --git a/src/network.c b/src/network.c index abfa1fb..93601f6 100644 --- a/src/network.c +++ b/src/network.c @@ -19,7 +19,7 @@ fd_set testfds, clientfds; char *msg_buf; size_t msg_size; -int port = 5000; +int port = 45011; int fd; int sockfd; FILE *sockstream; diff --git a/src/server.c b/src/server.c index fd8ba5c..57c6c03 100644 --- a/src/server.c +++ b/src/server.c @@ -293,7 +293,7 @@ int main(int argc, char *argv[]) { pthread_t tid; /* Socket settings */ - int port = 5000; + const int port = 45011; listenfd = socket(AF_INET, SOCK_STREAM, 0); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); From 0a5277b374526420cf57251167c6de3d5d193fde Mon Sep 17 00:00:00 2001 From: Evan Lloyd New-Schmidt Date: Sun, 30 Aug 2020 14:18:11 -0700 Subject: [PATCH 4/4] Unconst port int Oddly the increased compiler warnings didn't reveal this issue... the iteration on line 308 gave me a warning in my editor. --- src/server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server.c b/src/server.c index 57c6c03..fc35869 100644 --- a/src/server.c +++ b/src/server.c @@ -293,7 +293,7 @@ int main(int argc, char *argv[]) { pthread_t tid; /* Socket settings */ - const int port = 45011; + int port = 45011; listenfd = socket(AF_INET, SOCK_STREAM, 0); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);