github.com/tcnksm/go@v0.0.0-20141208075154-439b32936367/src/net/mockicmp_test.go (about)

     1  // Copyright 2009 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package net
     6  
     7  import "errors"
     8  
     9  const (
    10  	icmpv4EchoRequest = 8
    11  	icmpv4EchoReply   = 0
    12  	icmpv6EchoRequest = 128
    13  	icmpv6EchoReply   = 129
    14  )
    15  
    16  // icmpMessage represents an ICMP message.
    17  type icmpMessage struct {
    18  	Type     int             // type
    19  	Code     int             // code
    20  	Checksum int             // checksum
    21  	Body     icmpMessageBody // body
    22  }
    23  
    24  // icmpMessageBody represents an ICMP message body.
    25  type icmpMessageBody interface {
    26  	Len() int
    27  	Marshal() ([]byte, error)
    28  }
    29  
    30  // Marshal returns the binary enconding of the ICMP echo request or
    31  // reply message m.
    32  func (m *icmpMessage) Marshal() ([]byte, error) {
    33  	b := []byte{byte(m.Type), byte(m.Code), 0, 0}
    34  	if m.Body != nil && m.Body.Len() != 0 {
    35  		mb, err := m.Body.Marshal()
    36  		if err != nil {
    37  			return nil, err
    38  		}
    39  		b = append(b, mb...)
    40  	}
    41  	switch m.Type {
    42  	case icmpv6EchoRequest, icmpv6EchoReply:
    43  		return b, nil
    44  	}
    45  	csumcv := len(b) - 1 // checksum coverage
    46  	s := uint32(0)
    47  	for i := 0; i < csumcv; i += 2 {
    48  		s += uint32(b[i+1])<<8 | uint32(b[i])
    49  	}
    50  	if csumcv&1 == 0 {
    51  		s += uint32(b[csumcv])
    52  	}
    53  	s = s>>16 + s&0xffff
    54  	s = s + s>>16
    55  	// Place checksum back in header; using ^= avoids the
    56  	// assumption the checksum bytes are zero.
    57  	b[2] ^= byte(^s)
    58  	b[3] ^= byte(^s >> 8)
    59  	return b, nil
    60  }
    61  
    62  // parseICMPMessage parses b as an ICMP message.
    63  func parseICMPMessage(b []byte) (*icmpMessage, error) {
    64  	msglen := len(b)
    65  	if msglen < 4 {
    66  		return nil, errors.New("message too short")
    67  	}
    68  	m := &icmpMessage{Type: int(b[0]), Code: int(b[1]), Checksum: int(b[2])<<8 | int(b[3])}
    69  	if msglen > 4 {
    70  		var err error
    71  		switch m.Type {
    72  		case icmpv4EchoRequest, icmpv4EchoReply, icmpv6EchoRequest, icmpv6EchoReply:
    73  			m.Body, err = parseICMPEcho(b[4:])
    74  			if err != nil {
    75  				return nil, err
    76  			}
    77  		}
    78  	}
    79  	return m, nil
    80  }
    81  
    82  // imcpEcho represenets an ICMP echo request or reply message body.
    83  type icmpEcho struct {
    84  	ID   int    // identifier
    85  	Seq  int    // sequence number
    86  	Data []byte // data
    87  }
    88  
    89  func (p *icmpEcho) Len() int {
    90  	if p == nil {
    91  		return 0
    92  	}
    93  	return 4 + len(p.Data)
    94  }
    95  
    96  // Marshal returns the binary enconding of the ICMP echo request or
    97  // reply message body p.
    98  func (p *icmpEcho) Marshal() ([]byte, error) {
    99  	b := make([]byte, 4+len(p.Data))
   100  	b[0], b[1] = byte(p.ID>>8), byte(p.ID)
   101  	b[2], b[3] = byte(p.Seq>>8), byte(p.Seq)
   102  	copy(b[4:], p.Data)
   103  	return b, nil
   104  }
   105  
   106  // parseICMPEcho parses b as an ICMP echo request or reply message
   107  // body.
   108  func parseICMPEcho(b []byte) (*icmpEcho, error) {
   109  	bodylen := len(b)
   110  	p := &icmpEcho{ID: int(b[0])<<8 | int(b[1]), Seq: int(b[2])<<8 | int(b[3])}
   111  	if bodylen > 4 {
   112  		p.Data = make([]byte, bodylen-4)
   113  		copy(p.Data, b[4:])
   114  	}
   115  	return p, nil
   116  }