github.com/gdamore/mangos@v1.4.0/conn.go (about)

     1  // Copyright 2018 The Mangos Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use file except in compliance with the License.
     5  // You may obtain a copy of the license at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package mangos
    16  
    17  import (
    18  	"encoding/binary"
    19  	"io"
    20  	"net"
    21  	"sync"
    22  )
    23  
    24  // conn implements the Pipe interface on top of net.Conn.  The
    25  // assumption is that transports using this have similar wire protocols,
    26  // and conn is meant to be used as a building block.
    27  type conn struct {
    28  	c     net.Conn
    29  	proto Protocol
    30  	sock  Socket
    31  	open  bool
    32  	props map[string]interface{}
    33  	maxrx int64
    34  	sync.Mutex
    35  }
    36  
    37  // connipc is *almost* like a regular conn, but the IPC protocol insists
    38  // on stuffing a leading byte (valued 1) in front of messages.  This is for
    39  // compatibility with nanomsg -- the value cannot ever be anything but 1.
    40  type connipc struct {
    41  	conn
    42  }
    43  
    44  // Recv implements the Pipe Recv method.  The message received is expected as
    45  // a 64-bit size (network byte order) followed by the message itself.
    46  func (p *conn) Recv() (*Message, error) {
    47  
    48  	var sz int64
    49  	var err error
    50  	var msg *Message
    51  
    52  	if err = binary.Read(p.c, binary.BigEndian, &sz); err != nil {
    53  		return nil, err
    54  	}
    55  
    56  	// Limit messages to the maximum receive value, if not
    57  	// unlimited.  This avoids a potential denaial of service.
    58  	if sz < 0 || (p.maxrx > 0 && sz > p.maxrx) {
    59  		return nil, ErrTooLong
    60  	}
    61  	msg = NewMessage(int(sz))
    62  	msg.Body = msg.Body[0:sz]
    63  	if _, err = io.ReadFull(p.c, msg.Body); err != nil {
    64  		msg.Free()
    65  		return nil, err
    66  	}
    67  	return msg, nil
    68  }
    69  
    70  // Send implements the Pipe Send method.  The message is sent as a 64-bit
    71  // size (network byte order) followed by the message itself.
    72  func (p *conn) Send(msg *Message) error {
    73  
    74  	l := uint64(len(msg.Header) + len(msg.Body))
    75  
    76  	if msg.Expired() {
    77  		msg.Free()
    78  		return nil
    79  	}
    80  
    81  	// send length header
    82  	if err := binary.Write(p.c, binary.BigEndian, l); err != nil {
    83  		return err
    84  	}
    85  	if _, err := p.c.Write(msg.Header); err != nil {
    86  		return err
    87  	}
    88  	// hope this works
    89  	if _, err := p.c.Write(msg.Body); err != nil {
    90  		return err
    91  	}
    92  	msg.Free()
    93  	return nil
    94  }
    95  
    96  // LocalProtocol returns our local protocol number.
    97  func (p *conn) LocalProtocol() uint16 {
    98  	return p.proto.Number()
    99  }
   100  
   101  // RemoteProtocol returns our peer's protocol number.
   102  func (p *conn) RemoteProtocol() uint16 {
   103  	return p.proto.PeerNumber()
   104  }
   105  
   106  // Close implements the Pipe Close method.
   107  func (p *conn) Close() error {
   108  	p.Lock()
   109  	defer p.Unlock()
   110  	if p.IsOpen() {
   111  		p.open = false
   112  		return p.c.Close()
   113  	}
   114  	return nil
   115  }
   116  
   117  // IsOpen implements the PipeIsOpen method.
   118  func (p *conn) IsOpen() bool {
   119  	return p.open
   120  }
   121  
   122  func (p *conn) GetProp(n string) (interface{}, error) {
   123  	if v, ok := p.props[n]; ok {
   124  		return v, nil
   125  	}
   126  	return nil, ErrBadProperty
   127  }
   128  
   129  // NewConnPipe allocates a new Pipe using the supplied net.Conn, and
   130  // initializes it.  It performs the handshake required at the SP layer,
   131  // only returning the Pipe once the SP layer negotiation is complete.
   132  //
   133  // Stream oriented transports can utilize this to implement a Transport.
   134  // The implementation will also need to implement PipeDialer, PipeAccepter,
   135  // and the Transport enclosing structure.   Using this layered interface,
   136  // the implementation needn't bother concerning itself with passing actual
   137  // SP messages once the lower layer connection is established.
   138  func NewConnPipe(c net.Conn, sock Socket, props ...interface{}) (Pipe, error) {
   139  	p := &conn{c: c, proto: sock.GetProtocol(), sock: sock}
   140  
   141  	if err := p.handshake(props); err != nil {
   142  		return nil, err
   143  	}
   144  
   145  	return p, nil
   146  }
   147  
   148  // connHeader is exchanged during the initial handshake.
   149  type connHeader struct {
   150  	Zero    byte // must be zero
   151  	S       byte // 'S'
   152  	P       byte // 'P'
   153  	Version byte // only zero at present
   154  	Proto   uint16
   155  	Rsvd    uint16 // always zero at present
   156  }
   157  
   158  // handshake establishes an SP connection between peers.  Both sides must
   159  // send the header, then both sides must wait for the peer's header.
   160  // As a side effect, the peer's protocol number is stored in the conn.
   161  // Also, various properties are initialized.
   162  func (p *conn) handshake(props []interface{}) error {
   163  	var err error
   164  
   165  	p.props = make(map[string]interface{})
   166  	p.props[PropLocalAddr] = p.c.LocalAddr()
   167  	p.props[PropRemoteAddr] = p.c.RemoteAddr()
   168  
   169  	for len(props) >= 2 {
   170  		switch name := props[0].(type) {
   171  		case string:
   172  			p.props[name] = props[1]
   173  		default:
   174  			return ErrBadProperty
   175  		}
   176  		props = props[2:]
   177  	}
   178  
   179  	if v, e := p.sock.GetOption(OptionMaxRecvSize); e == nil {
   180  		// socket guarantees this is an integer
   181  		p.maxrx = int64(v.(int))
   182  	}
   183  
   184  	h := connHeader{S: 'S', P: 'P', Proto: p.proto.Number()}
   185  	if err = binary.Write(p.c, binary.BigEndian, &h); err != nil {
   186  		return err
   187  	}
   188  	if err = binary.Read(p.c, binary.BigEndian, &h); err != nil {
   189  		p.c.Close()
   190  		return err
   191  	}
   192  	if h.Zero != 0 || h.S != 'S' || h.P != 'P' || h.Rsvd != 0 {
   193  		p.c.Close()
   194  		return ErrBadHeader
   195  	}
   196  	// The only version number we support at present is "0", at offset 3.
   197  	if h.Version != 0 {
   198  		p.c.Close()
   199  		return ErrBadVersion
   200  	}
   201  
   202  	// The protocol number lives as 16-bits (big-endian) at offset 4.
   203  	if h.Proto != p.proto.PeerNumber() {
   204  		p.c.Close()
   205  		return ErrBadProto
   206  	}
   207  	p.open = true
   208  	return nil
   209  }