On Tue, 10 Mar 2020 at 23:14, Ian Lance Taylor <i...@golang.org> wrote:

> On Tue, Mar 10, 2020 at 4:03 PM Tom Parkin <tom.par...@gmail.com> wrote:
> >
> > I'm working on adding a new Linux socket type (L2TPIP) to the unix
> package, and I noticed some code in there that appears on the face of it to
> be assuming the endianess of the host.  The #networking channel on the
> Gophers slack suggested I raise the question here.
> >
> > The code I am struggling with is in this file:
> >
> > https://github.com/golang/sys/blob/master/unix/syscall_linux.go
> >
> > So far as I can make out, this code implements a variety of system calls
> for Linux generally, irrespective of GOARCH.
> >
> > In this file, there is a function anyToSockaddr which serves to convert
> struct sockaddr from system calls such as getsockname(2) and accept4(2)
> into Go representations of the various sockaddr types, for example
> unix.SockaddrUnix and unix.SockaddrInet4.  The function anyToSockaddr
> switches on the address family in the struct sockaddr, and then converts
> based on that.
> >
> > I noticed for the AF_INET case in the switch statement of anyToSockaddr
> that the struct sockaddr_in sin_port field is being unconditionally
> byte-swapped during conversion:
> >
> >             pp := (*RawSockaddrInet4)(unsafe.Pointer(rsa))
> >             sa := new(SockaddrInet4)
> >             p := (*[2]byte)(unsafe.Pointer(&pp.Port))
> >             sa.Port = int(p[0])<<8 + int(p[1])
> >             for i := 0; i < len(sa.Addr); i++ {
> >                 sa.Addr[i] = pp.Addr[i]
> >             }
> >             return sa, nil
> >
> > (where 'rsa' is a pointer to a RawSockaddrAny).
> >
> > Now, ip(7) states of the struct sockaddr_in structure:
> >
> >   "Note that the address and the port are always stored in network byte
> order.  In particular, this means that you need to call htons(3) on the
> number that is assigned to a port."
> >
> > So the byte swapping that anyToSockaddr is doing makes sense, but only
> if the host is a little-endian machine.  It seems as though this code would
> do the wrong thing on a big-endian machine.
> >
> > Can anyone suggest what I'm missing here?  Is this code really assuming
> that the host is a little-endian machine?
>
> This does not look like byte swapping to me.  Here pp.Port should be
> in network byte order.  To set sa.Port we read the first byte of
> pp.Port, left shift by 8, then or in the second byte of pp.Port.  That
> is, we interpret pp.Port as a two-byte big-endian number, and compute
> the value as a 16-bit integer.  That will work regardless of the
> endianness of the host.
>

Thanks Ian for your answer.

It took me a little bit of thinking to get there :-( but I see what you're
saying now.

For anyone else playing along at home who may be struggling like I was...

By treating the uint16 pp.Port value as an array of bytes, the code can
access the bytes in network byte order, since that's how byte arrays are
laid out in memory (e.g. if &p[0] is address A, &p[1] is A+1, etc).

The left shift and addition effectively convert to host byte order, since
that's how uint16 value will be stored in memory.

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/CACzvQmaCcqfuZNyX43-%2Bb51z6bK8LLMrc7VxtLCMctBSg_fkJg%40mail.gmail.com.

Reply via email to