Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

socket: long options #75

Merged
merged 5 commits into from
Jun 28, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 37 additions & 23 deletions imports/wasi_snapshot_preview1/wasmedge.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ var WasmEdgeV1 = Extension{
"sock_send_to": wazergo.F6((*Module).WasmEdgeSockSendTo),
"sock_recv_from": wazergo.F6((*Module).WasmEdgeV1SockRecvFrom),
"sock_getsockopt": wazergo.F5((*Module).WasmEdgeSockGetOpt),
"sock_setsockopt": wazergo.F5((*Module).WasmEdgeSockSetOpt),
"sock_setsockopt": wazergo.F4((*Module).WasmEdgeSockSetOpt),
"sock_getlocaladdr": wazergo.F4((*Module).WasmEdgeV1SockLocalAddr),
"sock_getpeeraddr": wazergo.F4((*Module).WasmEdgeV1SockPeerAddr),
"sock_getaddrinfo": wazergo.F6((*Module).WasmEdgeSockAddrInfo),
Expand All @@ -42,7 +42,7 @@ var WasmEdgeV2 = Extension{
"sock_send_to": wazergo.F6((*Module).WasmEdgeSockSendTo),
"sock_recv_from": wazergo.F7((*Module).WasmEdgeV2SockRecvFrom),
"sock_getsockopt": wazergo.F5((*Module).WasmEdgeSockGetOpt),
"sock_setsockopt": wazergo.F5((*Module).WasmEdgeSockSetOpt),
"sock_setsockopt": wazergo.F4((*Module).WasmEdgeSockSetOpt),
"sock_getlocaladdr": wazergo.F3((*Module).WasmEdgeV2SockLocalAddr),
"sock_getpeeraddr": wazergo.F3((*Module).WasmEdgeV2SockPeerAddr),
"sock_getaddrinfo": wazergo.F6((*Module).WasmEdgeSockAddrInfo),
Expand Down Expand Up @@ -131,40 +131,54 @@ func (m *Module) WasmEdgeV2SockRecvFrom(ctx context.Context, fd Int32, iovecs Li
return Errno(wasi.ESUCCESS)
}

func (m *Module) WasmEdgeSockSetOpt(ctx context.Context, fd Int32, level Int32, option Int32, value Pointer[Int32], valueLen Int32) Errno {
// See socket.go
switch wasi.SocketOptionLevel(level) {
case wasi.TcpLevel:
option += 0x1000
}
// Only int options are supported for now.
switch wasi.SocketOption(option) {
case wasi.Linger, wasi.RecvTimeout, wasi.SendTimeout, wasi.BindToDevice:
// These accept struct linger / struct timeval / string.
func (m *Module) WasmEdgeSockSetOpt(ctx context.Context, fd Int32, level Int32, option Int32, value Bytes) Errno {
opt := wasi.SocketOption((int64(level) << 32) | int64(option))
pelletier marked this conversation as resolved.
Show resolved Hide resolved

var val wasi.SocketOptionValue
switch opt {
case wasi.ReuseAddress,
wasi.DontRoute,
wasi.Broadcast,
wasi.SendBufferSize,
wasi.RecvBufferSize,
wasi.KeepAlive,
wasi.OOBInline,
wasi.TcpNoDelay:

if len(value) != 4 {
return Errno(wasi.EINVAL)
}
val = wasi.IntValue(binary.LittleEndian.Uint32(value))
case wasi.Linger,
wasi.RecvTimeout,
wasi.SendTimeout,
wasi.BindToDevice:
return Errno(wasi.ENOTSUP)
case wasi.QuerySocketType,
wasi.QuerySocketError,
wasi.QueryAcceptConnections:
return Errno(wasi.ENOTSUP)
chriso marked this conversation as resolved.
Show resolved Hide resolved

default:
val = wasi.StringValue(value)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have a BytesValue instead? I feel like StringValue implies it's UTF-8 but since this is a fallback it could be anything.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the level of the stack we're operating at, StringValue implies that it's a null-terminated string rather than a utf-8 encoded string. BytesValue to me reads that it may contain a null byte and so length is supplied separately.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At this point of the code we failed to recognized the option and will just pass the bytes as-is to the wasi.System, so it may contain null bytes?

}
if valueLen != 4 {
return Errno(wasi.EINVAL)
}
return Errno(m.WASI.SockSetOpt(ctx, wasi.FD(fd), wasi.SocketOptionLevel(level), wasi.SocketOption(option), wasi.IntValue(value.Load())))

return Errno(m.WASI.SockSetOpt(ctx, wasi.FD(fd), opt, val))
}

func (m *Module) WasmEdgeSockGetOpt(ctx context.Context, fd Int32, level Int32, option Int32, value Pointer[Int32], valueLen Int32) Errno {
// See socket.go
switch wasi.SocketOptionLevel(level) {
case wasi.TcpLevel:
option += 0x1000
}
opt := wasi.SocketOption((int64(level) << 32) | int64(option))

// Only int options are supported for now.
switch wasi.SocketOption(option) {
switch opt {
case wasi.Linger, wasi.RecvTimeout, wasi.SendTimeout, wasi.BindToDevice:
// These accept struct linger / struct timeval / string.
return Errno(wasi.ENOTSUP)
}
if valueLen != 4 {
return Errno(wasi.EINVAL)
}
result, errno := m.WASI.SockGetOpt(ctx, wasi.FD(fd), wasi.SocketOptionLevel(level), wasi.SocketOption(option))
result, errno := m.WASI.SockGetOpt(ctx, wasi.FD(fd), opt)
if errno != wasi.ESUCCESS {
return Errno(errno)
}
Expand Down
29 changes: 23 additions & 6 deletions socket.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,17 +326,23 @@ func (sl SocketOptionLevel) String() string {
switch sl {
case SocketLevel:
return "SocketLevel"
case TcpLevel:
return "TcpLevel"
default:
return fmt.Sprintf("SocketOptionLevel(%d)", sl)
}
}

// SocketOption is a socket option that can be queried or set.
type SocketOption int32
type SocketOption int64

func (s SocketOption) Level() SocketOptionLevel {
return SocketOptionLevel(s >> 32)
}

// SOL_SOCKET level options.
const (
// SOL_SOCKET level options.
ReuseAddress SocketOption = iota
ReuseAddress SocketOption = (SocketOption(SocketLevel) << 32) | iota
QuerySocketType
QuerySocketError
DontRoute
Expand All @@ -351,9 +357,11 @@ const (
SendTimeout
QueryAcceptConnections
BindToDevice
)

// 0x1000 + iota are IPPROTO_TCP level options.
TcpNoDelay SocketOption = 0x1000 + iota
// IPPROTO_TCP level options
const (
TcpNoDelay SocketOption = (SocketOption(TcpLevel) << 32) | (15)
)

func (so SocketOption) String() string {
Expand Down Expand Up @@ -391,7 +399,7 @@ func (so SocketOption) String() string {
case TcpNoDelay:
return "TcpNoDelay"
default:
return fmt.Sprintf("SocketOption(%d)", so)
return fmt.Sprintf("SocketOption(%d|%d)", so.Level(), int32(so))
}
}

Expand Down Expand Up @@ -480,6 +488,15 @@ func (tv TimeValue) String() string {
return time.Duration(tv).String()
}

// StringValue is used to represent an arbitrary socket option value.
type StringValue string

func (StringValue) sockopt() {}

func (s StringValue) String() string {
return string(s)
}

// SocketsNotSupported is a helper type intended to be embeded in
// implementations of the Sytem interface that do not support sockets.
//
Expand Down
4 changes: 2 additions & 2 deletions system.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,12 +363,12 @@ type System interface {
// SockGetOpt gets a socket option.
//
// Note: This is similar to getsockopt in POSIX.
SockGetOpt(ctx context.Context, fd FD, level SocketOptionLevel, option SocketOption) (SocketOptionValue, Errno)
SockGetOpt(ctx context.Context, fd FD, option SocketOption) (SocketOptionValue, Errno)

// SockSetOpt sets a socket option.
//
// Note: This is similar to setsockopt in POSIX.
SockSetOpt(ctx context.Context, fd FD, level SocketOptionLevel, option SocketOption, value SocketOptionValue) Errno
SockSetOpt(ctx context.Context, fd FD, option SocketOption, value SocketOptionValue) Errno

// SockLocalAddress gets the local address of the socket.
//
Expand Down
8 changes: 4 additions & 4 deletions systems/unix/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -669,14 +669,14 @@ func (s *System) SockRecvFrom(ctx context.Context, fd wasi.FD, iovecs []wasi.IOV
}
}

func (s *System) SockGetOpt(ctx context.Context, fd wasi.FD, level wasi.SocketOptionLevel, option wasi.SocketOption) (wasi.SocketOptionValue, wasi.Errno) {
func (s *System) SockGetOpt(ctx context.Context, fd wasi.FD, option wasi.SocketOption) (wasi.SocketOptionValue, wasi.Errno) {
socket, _, errno := s.LookupSocketFD(fd, 0)
if errno != wasi.ESUCCESS {
return nil, errno
}

var sysLevel int
switch level {
switch option.Level() {
case wasi.SocketLevel:
sysLevel = unix.SOL_SOCKET
case wasi.TcpLevel:
Expand Down Expand Up @@ -769,14 +769,14 @@ func (s *System) SockGetOpt(ctx context.Context, fd wasi.FD, level wasi.SocketOp
return wasi.IntValue(value), errno
}

func (s *System) SockSetOpt(ctx context.Context, fd wasi.FD, level wasi.SocketOptionLevel, option wasi.SocketOption, value wasi.SocketOptionValue) wasi.Errno {
func (s *System) SockSetOpt(ctx context.Context, fd wasi.FD, option wasi.SocketOption, value wasi.SocketOptionValue) wasi.Errno {
socket, _, errno := s.LookupSocketFD(fd, 0)
if errno != wasi.ESUCCESS {
return errno
}

var sysLevel int
switch level {
switch option.Level() {
case wasi.SocketLevel:
sysLevel = unix.SOL_SOCKET
case wasi.TcpLevel:
Expand Down
12 changes: 6 additions & 6 deletions tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -680,9 +680,9 @@ func (t *tracer) SockRecvFrom(ctx context.Context, fd FD, iovecs []IOVec, iflags
return n, oflags, addr, errno
}

func (t *tracer) SockGetOpt(ctx context.Context, fd FD, level SocketOptionLevel, option SocketOption) (SocketOptionValue, Errno) {
t.printf("SockGetOpt(%d, %s, %s) => ", fd, level, option)
value, errno := t.system.SockGetOpt(ctx, fd, level, option)
func (t *tracer) SockGetOpt(ctx context.Context, fd FD, option SocketOption) (SocketOptionValue, Errno) {
t.printf("SockGetOpt(%d, %s) => ", fd, option)
value, errno := t.system.SockGetOpt(ctx, fd, option)
if errno == ESUCCESS {
t.printf("%d", value)
} else {
Expand All @@ -692,9 +692,9 @@ func (t *tracer) SockGetOpt(ctx context.Context, fd FD, level SocketOptionLevel,
return value, errno
}

func (t *tracer) SockSetOpt(ctx context.Context, fd FD, level SocketOptionLevel, option SocketOption, value SocketOptionValue) Errno {
t.printf("SockSetOpt(%d, %s, %s, %d) => ", fd, level, option, value)
errno := t.system.SockSetOpt(ctx, fd, level, option, value)
func (t *tracer) SockSetOpt(ctx context.Context, fd FD, option SocketOption, value SocketOptionValue) Errno {
t.printf("SockSetOpt(%d, %s, %s) => ", fd, option, value)
errno := t.system.SockSetOpt(ctx, fd, option, value)
if errno == ESUCCESS {
t.printf("ok")
} else {
Expand Down
Loading