Right way to convert syscall.Sockaddr -> C.struct_sockaddr ?

906 views
Skip to first unread message

Juan Batiz-Benet

unread,
Mar 10, 2014, 8:51:20 PM3/10/14
to golan...@googlegroups.com
What's the correct, portable way to convert between a `syscall.Sockaddr` and `C.struct_sockaddr`? 
(Why? am implementing bindings for a c lib.)


Looking through the net and syscall packages, I see

`syscall.RawSockaddr

syscall.SockaddrX have raw (unexported) ptrs to it. 
The raw ptrs seem to get populated when needed. 
In some platforms, they're even the same:

type RawSockaddr C.struct_sockaddr

Having a RawSockaddr, one can cast it using `unsafe.Pointer` (seems awful to me).
But can't even find an exported function to turn a Sockaddr -> RawSockaddr portably.


## Unix implementations seem to use (unexported) `Sockaddr.sockaddr()`:

type Sockaddr interface {
  sockaddr() (ptr uintptr, len _Socklen, err error) // lowercase; only we can define Sockaddrs
}

func Connect(fd int, sa Sockaddr) (err error) {
  ptr, n, err := sa.sockaddr()
  if err != nil {
    return err
  }
  return connect(fd, ptr, n)
}

Even windows seems to work the same way:

type Sockaddr interface {
  sockaddr() (ptr uintptr, len int32, err error) // lowercase; only we can define Sockaddrs
}


It seems this is a better way to go. But again, this is not exported.
What's the _right_ way to do this?

Juan Batiz-Benet

unread,
Mar 11, 2014, 3:03:54 AM3/11/14
to golan...@googlegroups.com, r...@golang.org
Ross, Go Team, any guidance here?

Might someone help me understand why `SockaddrX.sockaddr()` is kept private?

It's meant to be kept private, as it's been so since its beginning (https://github.com/jnwhiteh/golang/commit/f998fa759dc2231b87a9e8177f51f1eeaf1c3735). I also dislike the sockets interface, would much rather have a UDPConn than a socket, and love that sockets have been hidden from Go as much as possible. But, I still think converting a Sockaddr to a RawSockaddr (and thus to a C.struct_sockaddr) should be something users should be able to do. Are there strong reasons to disallow this?

To explain why I need it: I want functionality the net package has to implement a transport protocol.[1] To expose a net-like UTPConn type, I need the underlying platform-specific address conversions. I could clone net, or copy the surprisingly large amount of code I need, but this IMO is the wrong approach. No changes as Go evolves would be accounted for, including possible bugfixes, etc.

[1] uTP. It is technically on top of UDP, but I'm using cgo + libutp, which means I deal with a socket-like interface, not a nice UDPConn. My plan is to build a Go idiomatic interface on top of libutp first, get it working, and in the future reevaluate reimplementing uTP in Go natively. (This is much more work than I can take on atm).

Ian Lance Taylor

unread,
Mar 11, 2014, 12:57:21 PM3/11/14
to Juan Batiz-Benet, golang-nuts, Russ Cox
On Tue, Mar 11, 2014 at 12:03 AM, Juan Batiz-Benet <ju...@benet.ai> wrote:
>
> I also dislike the sockets interface, would much rather have a UDPConn than
> a socket, and love that sockets have been hidden from Go as much as
> possible. But, I still think converting a Sockaddr to a RawSockaddr (and
> thus to a C.struct_sockaddr) should be something users should be able to do.
> Are there strong reasons to disallow this?

It's an inherently unsafe operation, and it's easy to do using
unsafe.Pointer. I don't think we need to change anything here.

raw := (*RawSockaddr)(unsafe.Pointer(s))

Ian

Juan Batiz-Benet

unread,
Mar 12, 2014, 9:48:42 PM3/12/14
to Ian Lance Taylor, golang-nuts, Russ Cox
Hey Ian, thanks for the response.

Sadly, what you suggest is incorrect.

What would that mean?  `Sockaddrs` (go types) and `RawSockaddrs` (c types) use different primitives, different fields altogether, and probably aligned differently across archs. Your unsafe cast would succeed and you'd end up with an incorrect value. See this example (Go playground's limitations include syscall.RawSockaddr definitions):

Notice, in my darwin 386 output (reproduced below for convenience) that `Sockaddr.{Port, Addr}` get read out as `RawSockaddr.{Family, Zero}`:
```
SockaddrInet4
  Port:   9200
  Addr:   [127 0 0 1]
RawSockaddrInet4
  Family: 35
  Port:   0
  Addr:   [0 0 0 0]
  Zero:   [127 0 0 1 0 0 0 0]
```

The conversions are complicated and platform dependent , as you can see in the links I posted before. Here again:


Unless I'm missing something:
- I don't think there is a way to do this currently. 
- This issue would be resolved by exporting the `Sockaddr.sockaddr` functions in a future Go release.
- Until then, I've resolved to extract these conversions out into a package. Will post again when that's done.

(As a side note, I think it would be best for all of us if we make sure to test code ourselves before suggesting it, ensuring that no misleading code is thrown around + would be responders wrongly assume the issue is resolved. I don't mean to offend, I very much appreciate your response and attempt to help :) -- simply noting that statements which are both dismissive and incorrect in a wide mailing list are likely to mislead a lot of readers.)

Thanks!
Juan

Russ Cox

unread,
Mar 12, 2014, 10:55:13 PM3/12/14
to Juan Batiz-Benet, Ian Lance Taylor, golang-nuts
To start with, RawSockaddrXxxx is identical in memory layout to C.struct_sockaddrxxx. I think you know this but I want to be very clear to anyone reading this.

There is no exported code that exposes the conversion from SockaddrXxx to RawSockaddrXxx. The SockaddrXxx structs and the Sockaddr interface they implement exist so to support the syscall APIs. If you are calling those APIs, start with a SockaddrXxx. If you are making system calls directly, start with a RawSockaddrXxx and use unsafe.Pointer as Ian has suggested to convert from RawSockaddrXxx to C.struct_sockaddrxxx and back.

If you must convert from Sockaddr to RawSockaddr, then copy the conversion code into your package. It's just a few lines and it's not going to change. That avoids increasing the already large surface of package syscall and is a near-zero amount of duplication. I don't think it is surprisingly large, and you certainly don't have to clone package net.

Russ
Reply all
Reply to author
Forward
0 new messages