diff options
| author | Tom Smeding <tom.smeding@gmail.com> | 2020-06-28 22:18:57 +0200 | 
|---|---|---|
| committer | Tom Smeding <tom.smeding@gmail.com> | 2020-06-28 22:18:57 +0200 | 
| commit | b85c1a60bf7f27e37fe2a86b26aa83ff198e208e (patch) | |
| tree | eac45cf0a5c0f24ac04f6d07dd20504370cd0af9 | |
| parent | f2f4f0d94a5280e15d89c25832825bb3ce65d2fd (diff) | |
server/protocol: Protocol version negotiation
| -rw-r--r-- | TODO.txt | 4 | ||||
| -rw-r--r-- | command.c | 24 | ||||
| -rw-r--r-- | command.h | 4 | ||||
| -rw-r--r-- | conn_data.c | 2 | ||||
| -rw-r--r-- | conn_data.h | 1 | ||||
| -rw-r--r-- | main.c | 2 | ||||
| -rw-r--r-- | protocol.md | 27 | 
7 files changed, 63 insertions, 1 deletions
| @@ -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 @@ -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; @@ -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  }; @@ -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`. | 
