aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Smeding <tom.smeding@gmail.com>2020-06-28 22:18:57 +0200
committerTom Smeding <tom.smeding@gmail.com>2020-06-28 22:18:57 +0200
commitb85c1a60bf7f27e37fe2a86b26aa83ff198e208e (patch)
treeeac45cf0a5c0f24ac04f6d07dd20504370cd0af9
parentf2f4f0d94a5280e15d89c25832825bb3ce65d2fd (diff)
server/protocol: Protocol version negotiation
-rw-r--r--TODO.txt4
-rw-r--r--command.c24
-rw-r--r--command.h4
-rw-r--r--conn_data.c2
-rw-r--r--conn_data.h1
-rw-r--r--main.c2
-rw-r--r--protocol.md27
7 files changed, 63 insertions, 1 deletions
diff --git a/TODO.txt b/TODO.txt
index 7771749..8e7ac3b 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -4,3 +4,7 @@
- Message replies: referenced message should be msgid
- Somehow communicate (push? poll?) to room members whether a particular user is currently _active_, not just online.
- A session is marked inactive 2 minutes after the latest activity on that session. Make the number "2" configurable and not a hard-coded constant.
+- Use poll(2), not select(2)
+- Fix OOM dos vector
+- Sub-linear hash table of conn_data's in main.c
+- Constant-time lookup of commands in command.c
diff --git a/command.c b/command.c
index 5121ddf..6e73ce3 100644
--- a/command.c
+++ b/command.c
@@ -25,6 +25,22 @@ struct cmd_retval{
#define RET_MEMZERO ((struct cmd_retval){.socket_close=false,.memzero=true})
#define RET_MEMZERO_CLOSE(close_) ((struct cmd_retval){.socket_close=(close_),.memzero=true})
+static struct cmd_retval cmd_version(struct conn_data *data,const char *tag,const char **args){
+ i64 version;
+ if (!parse_i64(args[0], &version)
+ || version < MIN_SUPPORTED_PROTOCOL_VERSION
+ || version > PROTOCOL_VERSION) {
+ data->protversion = -1;
+ net_send_error(data->fd, tag, "Version not supported");
+ return RET_OK;
+ }
+
+ data->protversion = version;
+
+ net_send_ok(data->fd, tag);
+ return RET_OK;
+}
+
static struct cmd_retval cmd_register(struct conn_data *data,const char *tag,const char **args){
i64 userid=db_find_user(args[0]);
if(userid!=-1){
@@ -375,6 +391,7 @@ struct cmd_info{
};
static const struct cmd_info commands[]={
+ {"version",1,false,cmd_version},
{"register",2,true,cmd_register},
{"login",2,true,cmd_login},
{"logout",0,false,cmd_logout},
@@ -423,6 +440,13 @@ bool handle_input_line(struct conn_data *data,char *line,size_t linelen){
return true;
}
+ // Ensure first command is 'version'
+ if(data->protversion==-1&&strcmp(commands[cmdi].cmdname,"version")!=0){
+ debug("Command %s before version negotiation on connection %d",
+ commands[cmdi].cmdname,data->fd);
+ return true;
+ }
+
int nargs=commands[cmdi].nargs;
char *args[nargs];
size_t cursor=cmdlen+1;
diff --git a/command.h b/command.h
index 3b01f42..e1226a5 100644
--- a/command.h
+++ b/command.h
@@ -4,6 +4,10 @@
#include "conn_data.h"
+#define PROTOCOL_VERSION 1
+#define MIN_SUPPORTED_PROTOCOL_VERSION 1
+
+
// Returns true if socket should be closed.
// Modifies some bytes in `line`, AS WELL AS line[linelen]!
bool handle_input_line(struct conn_data *data,char *line,size_t linelen);
diff --git a/conn_data.c b/conn_data.c
index 1b237b4..fa2da10 100644
--- a/conn_data.c
+++ b/conn_data.c
@@ -7,6 +7,7 @@ void conn_data_init(struct conn_data *data,int fd){
data->buflen=0;
data->buffer=malloc(data->bufsz,char);
+ data->protversion=-1;
data->userid=-1;
}
@@ -16,5 +17,6 @@ void conn_data_nullify(struct conn_data *data){
data->bufsz=0;
data->buflen=0;
+ data->protversion=-1;
data->userid=-1;
}
diff --git a/conn_data.h b/conn_data.h
index dae100f..4c6ab07 100644
--- a/conn_data.h
+++ b/conn_data.h
@@ -8,6 +8,7 @@ struct conn_data{
i64 bufsz,buflen;
char *buffer;
+ i64 protversion; // -1 if not negotiated yet
i64 userid; // -1 if not logged in
};
diff --git a/main.c b/main.c
index 77237bf..78c98cb 100644
--- a/main.c
+++ b/main.c
@@ -125,6 +125,8 @@ static bool server_socket_callback(int fd){
}
static bool timeout_callback(int fd){
+ // Note: this does not check the protocol version. If the protocol changes
+ // significantly regarding push messages, perhaps that should change.
return net_send_raw_text(fd,"_push ping\n",11);
}
diff --git a/protocol.md b/protocol.md
index 4094b79..2d7ec63 100644
--- a/protocol.md
+++ b/protocol.md
@@ -1,4 +1,4 @@
-# tomsg protocol
+# tomsg protocol (version 1)
The underlying transport of the protocol is a plain TCP socket. The individual
messages are all line-based; this means that a single message, both
@@ -16,6 +16,11 @@ There are three kinds of messages: commands from the client, command responses
from the server, and push messages from the server (not to be confused with push
_notifications_). These three kinds are described below.
+Note that the first message the client must send is the `version` command, with
+which server and client can agree (or not) on a protocol version; if a `version`
+message has not yet been replied to with an `ok` response, no other commands are
+valid. See the definition of the `version` command for more information.
+
In the syntax descriptions below, `<name>` indicates an argument called "name";
the angle brackets are not part of the syntax. The type of the argument may be
indicated with the notation `<name:type>`; possible types are `i64` (a 64-bit
@@ -63,6 +68,26 @@ arguments are zero or more words, except if for the command in question the last
argument is of type `string...` (in which case it may contain spaces and
ranges until the end of the line).
+- `<tag> version <version:word>`
+ - Indicates that the client wishes to speak the protocol with the specified
+ version. For the version of the protocol described in this document, see the
+ header at the top of the file.
+
+ After the server has replied to this command with `ok`, the rest of the
+ protocol of (only) the specified version may be used. Before that, or if the
+ server replies with `error`, no commands other than `version` are valid.
+
+ If the client sends subsequent `version` messages after the first one, the
+ server may choose to handle those subsequent messages and switch versions,
+ or always return `error`, or some arbitrary mix. However, sending multiple
+ `version` messages is always valid (though not recommended).
+
+ It is probable that the server can only speak one version of the protocol,
+ but it may support multiple versions. Note also that the version is a
+ _word_, not an integer; though this document describes an integer-versioned
+ protocol, future versions may use other printable ASCII characters (but not
+ any outside that range).
+ - Returns `ok` or `error`.
- `<tag> register <user:word> <pass:string...>`
- Register a new user on the server. The password may contain spaces.
- Returns `ok` or `error`.