Blame


1 4b33cdd0 2015-11-30 stephen.d package p9p
2 4b33cdd0 2015-11-30 stephen.d
3 4b33cdd0 2015-11-30 stephen.d import (
4 4b33cdd0 2015-11-30 stephen.d "fmt"
5 4b33cdd0 2015-11-30 stephen.d
6 529e2b2e 2016-11-14 noreply "context"
7 4b33cdd0 2015-11-30 stephen.d )
8 4b33cdd0 2015-11-30 stephen.d
9 4b33cdd0 2015-11-30 stephen.d // NOTE(stevvooe): This file contains functions for negotiating version on the
10 4b33cdd0 2015-11-30 stephen.d // client and server. There are some nasty details to get right for
11 4b33cdd0 2015-11-30 stephen.d // downgrading the connection on the server-side that are not present yet.
12 4b33cdd0 2015-11-30 stephen.d // Really, these should be refactored into some sort of channel type that can
13 4b33cdd0 2015-11-30 stephen.d // support resets through version messages during the protocol exchange.
14 4b33cdd0 2015-11-30 stephen.d
15 4b33cdd0 2015-11-30 stephen.d // clientnegotiate negiotiates the protocol version using channel, blocking
16 4b33cdd0 2015-11-30 stephen.d // until a response is received. The received value will be the version
17 4b33cdd0 2015-11-30 stephen.d // implemented by the server.
18 4b33cdd0 2015-11-30 stephen.d func clientnegotiate(ctx context.Context, ch Channel, version string) (string, error) {
19 4b33cdd0 2015-11-30 stephen.d req := newFcall(NOTAG, MessageTversion{
20 4b33cdd0 2015-11-30 stephen.d MSize: uint32(ch.MSize()),
21 4b33cdd0 2015-11-30 stephen.d Version: version,
22 4b33cdd0 2015-11-30 stephen.d })
23 4b33cdd0 2015-11-30 stephen.d
24 4b33cdd0 2015-11-30 stephen.d if err := ch.WriteFcall(ctx, req); err != nil {
25 4b33cdd0 2015-11-30 stephen.d return "", err
26 4b33cdd0 2015-11-30 stephen.d }
27 4b33cdd0 2015-11-30 stephen.d
28 4b33cdd0 2015-11-30 stephen.d resp := new(Fcall)
29 4b33cdd0 2015-11-30 stephen.d if err := ch.ReadFcall(ctx, resp); err != nil {
30 4b33cdd0 2015-11-30 stephen.d return "", err
31 4b33cdd0 2015-11-30 stephen.d }
32 4b33cdd0 2015-11-30 stephen.d
33 4b33cdd0 2015-11-30 stephen.d switch v := resp.Message.(type) {
34 4b33cdd0 2015-11-30 stephen.d case MessageRversion:
35 4b33cdd0 2015-11-30 stephen.d
36 4b33cdd0 2015-11-30 stephen.d if v.Version != version {
37 4b33cdd0 2015-11-30 stephen.d // TODO(stevvooe): A stubborn client indeed!
38 4b33cdd0 2015-11-30 stephen.d return "", fmt.Errorf("unsupported server version: %v", version)
39 4b33cdd0 2015-11-30 stephen.d }
40 4b33cdd0 2015-11-30 stephen.d
41 f717cf67 2016-10-04 noreply if int(v.MSize) < ch.MSize() {
42 4b33cdd0 2015-11-30 stephen.d // upgrade msize if server differs.
43 4b33cdd0 2015-11-30 stephen.d ch.SetMSize(int(v.MSize))
44 4b33cdd0 2015-11-30 stephen.d }
45 4b33cdd0 2015-11-30 stephen.d
46 4b33cdd0 2015-11-30 stephen.d return v.Version, nil
47 4b33cdd0 2015-11-30 stephen.d case error:
48 4b33cdd0 2015-11-30 stephen.d return "", v
49 4b33cdd0 2015-11-30 stephen.d default:
50 4b33cdd0 2015-11-30 stephen.d return "", ErrUnexpectedMsg
51 4b33cdd0 2015-11-30 stephen.d }
52 4b33cdd0 2015-11-30 stephen.d }
53 4b33cdd0 2015-11-30 stephen.d
54 4b33cdd0 2015-11-30 stephen.d // servernegotiate blocks until a version message is received or a timeout
55 4b33cdd0 2015-11-30 stephen.d // occurs. The msize for the tranport will be set from the negotiation. If
56 4b33cdd0 2015-11-30 stephen.d // negotiate returns nil, a server may proceed with the connection.
57 4b33cdd0 2015-11-30 stephen.d //
58 4b33cdd0 2015-11-30 stephen.d // In the future, it might be better to handle the version messages in a
59 4b33cdd0 2015-11-30 stephen.d // separate object that manages the session. Each set of version requests
60 4b33cdd0 2015-11-30 stephen.d // effectively "reset" a connection, meaning all fids get clunked and all
61 4b33cdd0 2015-11-30 stephen.d // outstanding IO is aborted. This is probably slightly racy, in practice with
62 4b33cdd0 2015-11-30 stephen.d // a misbehaved client. The main issue is that we cannot tell which session
63 4b33cdd0 2015-11-30 stephen.d // messages belong to.
64 4b33cdd0 2015-11-30 stephen.d func servernegotiate(ctx context.Context, ch Channel, version string) error {
65 4b33cdd0 2015-11-30 stephen.d // wait for the version message over the transport.
66 4b33cdd0 2015-11-30 stephen.d req := new(Fcall)
67 4b33cdd0 2015-11-30 stephen.d if err := ch.ReadFcall(ctx, req); err != nil {
68 4b33cdd0 2015-11-30 stephen.d return err
69 4b33cdd0 2015-11-30 stephen.d }
70 4b33cdd0 2015-11-30 stephen.d
71 4b33cdd0 2015-11-30 stephen.d mv, ok := req.Message.(MessageTversion)
72 4b33cdd0 2015-11-30 stephen.d if !ok {
73 4b33cdd0 2015-11-30 stephen.d return fmt.Errorf("expected version message: %v", mv)
74 4b33cdd0 2015-11-30 stephen.d }
75 4b33cdd0 2015-11-30 stephen.d
76 4b33cdd0 2015-11-30 stephen.d respmsg := MessageRversion{
77 4b33cdd0 2015-11-30 stephen.d Version: version,
78 4b33cdd0 2015-11-30 stephen.d }
79 4b33cdd0 2015-11-30 stephen.d
80 4b33cdd0 2015-11-30 stephen.d if mv.Version != version {
81 4b33cdd0 2015-11-30 stephen.d // TODO(stevvooe): Not the best place to do version handling. We need
82 4b33cdd0 2015-11-30 stephen.d // to have a way to pass supported versions into this method then have
83 63c84aa9 2016-02-05 stevvooe // it return the actual version. For now, respond with 9P2000 for
84 4b33cdd0 2015-11-30 stephen.d // anything that doesn't match the provided version string.
85 63c84aa9 2016-02-05 stevvooe //
86 63c84aa9 2016-02-05 stevvooe // version(9) says "The server may respond with the client’s
87 63c84aa9 2016-02-05 stevvooe // version string, or a version string identifying an earlier
88 63c84aa9 2016-02-05 stevvooe // defined protocol version. Currently, the only defined
89 63c84aa9 2016-02-05 stevvooe // version is the 6 characters 9P2000." Therefore, it is always
90 63c84aa9 2016-02-05 stevvooe // OK to respond with this.
91 63c84aa9 2016-02-05 stevvooe respmsg.Version = "9P2000"
92 4b33cdd0 2015-11-30 stephen.d }
93 4b33cdd0 2015-11-30 stephen.d
94 4b33cdd0 2015-11-30 stephen.d if int(mv.MSize) < ch.MSize() {
95 4b33cdd0 2015-11-30 stephen.d // if the server msize is too large, use the client's suggested msize.
96 4b33cdd0 2015-11-30 stephen.d ch.SetMSize(int(mv.MSize))
97 4b33cdd0 2015-11-30 stephen.d respmsg.MSize = mv.MSize
98 4b33cdd0 2015-11-30 stephen.d } else {
99 4b33cdd0 2015-11-30 stephen.d respmsg.MSize = uint32(ch.MSize())
100 4b33cdd0 2015-11-30 stephen.d }
101 4b33cdd0 2015-11-30 stephen.d
102 4b33cdd0 2015-11-30 stephen.d resp := newFcall(NOTAG, respmsg)
103 4b33cdd0 2015-11-30 stephen.d if err := ch.WriteFcall(ctx, resp); err != nil {
104 4b33cdd0 2015-11-30 stephen.d return err
105 4b33cdd0 2015-11-30 stephen.d }
106 4b33cdd0 2015-11-30 stephen.d
107 4b33cdd0 2015-11-30 stephen.d if respmsg.Version == "unknown" {
108 4b33cdd0 2015-11-30 stephen.d return fmt.Errorf("bad version negotiation")
109 4b33cdd0 2015-11-30 stephen.d }
110 4b33cdd0 2015-11-30 stephen.d
111 4b33cdd0 2015-11-30 stephen.d return nil
112 4b33cdd0 2015-11-30 stephen.d }