github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/imports/wasi_snapshot_preview1/sock.go (about)

     1  package wasi_snapshot_preview1
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/bananabytelabs/wazero/api"
     8  	"github.com/bananabytelabs/wazero/experimental/sys"
     9  	socketapi "github.com/bananabytelabs/wazero/internal/sock"
    10  	"github.com/bananabytelabs/wazero/internal/sysfs"
    11  	"github.com/bananabytelabs/wazero/internal/wasip1"
    12  	"github.com/bananabytelabs/wazero/internal/wasm"
    13  )
    14  
    15  var sockSendTo = newHostFunc("sock_send_to", sockSendToFn, []wasm.ValueType{i32, i32, i32, i32, i32, i32, i32}, "foo", "bar", "baz", "a", "b", "v")
    16  
    17  func sockSendToFn(_ context.Context, mod api.Module, params []uint64) (errno sys.Errno) {
    18  	fmt.Println("sock send to")
    19  	return
    20  }
    21  
    22  var sockRecvFrom = newHostFunc("sock_recv_from", sockRecvFromFn, []wasm.ValueType{i32, i32, i32, i32, i32, i32, i32, i32}, "foo", "bar", "baz", "a", "b", "v", "d")
    23  
    24  func sockRecvFromFn(_ context.Context, mod api.Module, params []uint64) (errno sys.Errno) {
    25  	fmt.Println("sock recv from")
    26  	return
    27  }
    28  
    29  var sockGetPeerAddr = newHostFunc("sock_getpeeraddr", sockGetPeerAddrFn, []wasm.ValueType{i32, i32, i32}, "foo", "bar", "baz")
    30  
    31  func sockGetPeerAddrFn(_ context.Context, mod api.Module, params []uint64) (errno sys.Errno) {
    32  	fmt.Println("sock get peer addr")
    33  	return
    34  }
    35  
    36  var sockGetLocalAddr = newHostFunc("sock_getlocaladdr", sockGetLocalAddrFn, []wasm.ValueType{i32, i32, i32}, "foo", "bar", "baz")
    37  
    38  func sockGetLocalAddrFn(_ context.Context, mod api.Module, params []uint64) (errno sys.Errno) {
    39  	fmt.Println("sock get local addr")
    40  	return
    41  }
    42  
    43  var sockSetSockOpt = newHostFunc("sock_setsockopt", sockSetSockOptFn, []wasm.ValueType{i32, i32, i32, i32, i32}, "foo", "bar", "baz", "a", "b")
    44  
    45  func sockSetSockOptFn(_ context.Context, mod api.Module, params []uint64) (errno sys.Errno) {
    46  	fmt.Println("sock set opt")
    47  	return
    48  }
    49  
    50  var sockGetSockOpt = newHostFunc("sock_getsockopt", sockGetSockOptFn, []wasm.ValueType{i32, i32, i32, i32, i32}, "foo", "bar", "baz", "a", "b")
    51  
    52  func sockGetSockOptFn(_ context.Context, mod api.Module, params []uint64) (errno sys.Errno) {
    53  	fmt.Println("sock get opt")
    54  	return
    55  }
    56  
    57  var sockConnect = newHostFunc("sock_connect", sockConnectFn, []wasm.ValueType{i32, i32, i32}, "foo", "bar", "baz")
    58  
    59  func sockConnectFn(_ context.Context, mod api.Module, params []uint64) (errno sys.Errno) {
    60  	fmt.Println("sock connect opt")
    61  	return
    62  }
    63  
    64  var sockOpen = newHostFunc("sock_open", sockOpenFn, []wasm.ValueType{i32, i32, i32}, "foo", "bar", "baz")
    65  
    66  func sockOpenFn(_ context.Context, mod api.Module, params []uint64) (errno sys.Errno) {
    67  	fmt.Println("sock open")
    68  	return
    69  }
    70  
    71  // sockAccept is the WASI function named SockAcceptName which accepts a new
    72  // incoming connection.
    73  //
    74  // See: https://github.com/WebAssembly/WASI/blob/0ba0c5e2e37625ca5a6d3e4255a998dfaa3efc52/phases/snapshot/docs.md#sock_accept
    75  // and https://github.com/WebAssembly/WASI/pull/458
    76  var sockAccept = newHostFunc(
    77  	wasip1.SockAcceptName,
    78  	sockAcceptFn,
    79  	[]wasm.ValueType{i32, i32, i32},
    80  	"fd", "flags", "result.fd",
    81  )
    82  
    83  func sockAcceptFn(_ context.Context, mod api.Module, params []uint64) (errno sys.Errno) {
    84  	mem := mod.Memory()
    85  	fsc := mod.(*wasm.ModuleInstance).Sys.FS()
    86  
    87  	fd := int32(params[0])
    88  	flags := uint32(params[1])
    89  	resultFd := uint32(params[2])
    90  	nonblock := flags&uint32(wasip1.FD_NONBLOCK) != 0
    91  
    92  	var connFD int32
    93  	if connFD, errno = fsc.SockAccept(fd, nonblock); errno == 0 {
    94  		mem.WriteUint32Le(resultFd, uint32(connFD))
    95  	}
    96  	return
    97  }
    98  
    99  // sockRecv is the WASI function named SockRecvName which receives a
   100  // message from a socket.
   101  //
   102  // See: https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-sock_recvfd-fd-ri_data-iovec_array-ri_flags-riflags---errno-size-roflags
   103  var sockRecv = newHostFunc(
   104  	wasip1.SockRecvName,
   105  	sockRecvFn,
   106  	[]wasm.ValueType{i32, i32, i32, i32, i32, i32},
   107  	"fd", "ri_data", "ri_data_len", "ri_flags", "result.ro_datalen", "result.ro_flags",
   108  )
   109  
   110  func sockRecvFn(_ context.Context, mod api.Module, params []uint64) sys.Errno {
   111  	mem := mod.Memory()
   112  	fsc := mod.(*wasm.ModuleInstance).Sys.FS()
   113  
   114  	fd := int32(params[0])
   115  	riData := uint32(params[1])
   116  	riDataCount := uint32(params[2])
   117  	riFlags := uint8(params[3])
   118  	resultRoDatalen := uint32(params[4])
   119  	resultRoFlags := uint32(params[5])
   120  
   121  	var conn socketapi.TCPConn
   122  	if e, ok := fsc.LookupFile(fd); !ok {
   123  		return sys.EBADF // Not open
   124  	} else if conn, ok = e.File.(socketapi.TCPConn); !ok {
   125  		return sys.EBADF // Not a conn
   126  	}
   127  
   128  	if riFlags & ^(wasip1.RI_RECV_PEEK|wasip1.RI_RECV_WAITALL) != 0 {
   129  		return sys.ENOTSUP
   130  	}
   131  
   132  	if riFlags&wasip1.RI_RECV_PEEK != 0 {
   133  		// Each record in riData is of the form:
   134  		// type iovec struct { buf *uint8; bufLen uint32 }
   135  		// This means that the first `uint32` is a `buf *uint8`.
   136  		firstIovecBufAddr, ok := mem.ReadUint32Le(riData)
   137  		if !ok {
   138  			return sys.EINVAL
   139  		}
   140  		// Read bufLen
   141  		firstIovecBufLen, ok := mem.ReadUint32Le(riData + 4)
   142  		if !ok {
   143  			return sys.EINVAL
   144  		}
   145  		firstIovecBuf, ok := mem.Read(firstIovecBufAddr, firstIovecBufLen)
   146  		if !ok {
   147  			return sys.EINVAL
   148  		}
   149  		n, err := conn.Recvfrom(firstIovecBuf, sysfs.MSG_PEEK)
   150  		if err != 0 {
   151  			return err
   152  		}
   153  		mem.WriteUint32Le(resultRoDatalen, uint32(n))
   154  		mem.WriteUint16Le(resultRoFlags, 0)
   155  		return 0
   156  	}
   157  
   158  	// If riFlags&wasip1.RECV_WAITALL != 0 then we should
   159  	// do a blocking operation until all data has been retrieved;
   160  	// otherwise we are able to return earlier.
   161  	// For simplicity, we currently wait all regardless the flag.
   162  	bufSize, errno := readv(mem, riData, riDataCount, conn.Read)
   163  	if errno != 0 {
   164  		return errno
   165  	}
   166  	mem.WriteUint32Le(resultRoDatalen, bufSize)
   167  	mem.WriteUint16Le(resultRoFlags, 0)
   168  	return 0
   169  }
   170  
   171  // sockSend is the WASI function named SockSendName which sends a message
   172  // on a socket.
   173  //
   174  // See: https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-sock_sendfd-fd-si_data-ciovec_array-si_flags-siflags---errno-size
   175  var sockSend = newHostFunc(
   176  	wasip1.SockSendName,
   177  	sockSendFn,
   178  	[]wasm.ValueType{i32, i32, i32, i32, i32},
   179  	"fd", "si_data", "si_data_len", "si_flags", "result.so_datalen",
   180  )
   181  
   182  func sockSendFn(_ context.Context, mod api.Module, params []uint64) sys.Errno {
   183  	mem := mod.Memory()
   184  	fsc := mod.(*wasm.ModuleInstance).Sys.FS()
   185  
   186  	fd := int32(params[0])
   187  	siData := uint32(params[1])
   188  	siDataCount := uint32(params[2])
   189  	siFlags := uint32(params[3])
   190  	resultSoDatalen := uint32(params[4])
   191  
   192  	if siFlags != 0 {
   193  		return sys.ENOTSUP
   194  	}
   195  
   196  	var conn socketapi.TCPConn
   197  	if e, ok := fsc.LookupFile(fd); !ok {
   198  		return sys.EBADF // Not open
   199  	} else if conn, ok = e.File.(socketapi.TCPConn); !ok {
   200  		return sys.EBADF // Not a conn
   201  	}
   202  
   203  	bufSize, errno := writev(mem, siData, siDataCount, conn.Write)
   204  	if errno != 0 {
   205  		return errno
   206  	}
   207  	mem.WriteUint32Le(resultSoDatalen, bufSize)
   208  	return 0
   209  }
   210  
   211  // sockShutdown is the WASI function named SockShutdownName which shuts
   212  // down socket send and receive channels.
   213  //
   214  // See: https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-sock_shutdownfd-fd-how-sdflags---errno
   215  var sockShutdown = newHostFunc(wasip1.SockShutdownName, sockShutdownFn, []wasm.ValueType{i32, i32}, "fd", "how")
   216  
   217  func sockShutdownFn(_ context.Context, mod api.Module, params []uint64) sys.Errno {
   218  	fsc := mod.(*wasm.ModuleInstance).Sys.FS()
   219  
   220  	fd := int32(params[0])
   221  	how := uint8(params[1])
   222  
   223  	var conn socketapi.TCPConn
   224  	if e, ok := fsc.LookupFile(fd); !ok {
   225  		return sys.EBADF // Not open
   226  	} else if conn, ok = e.File.(socketapi.TCPConn); !ok {
   227  		return sys.EBADF // Not a conn
   228  	}
   229  
   230  	sysHow := 0
   231  
   232  	switch how {
   233  	case wasip1.SD_RD | wasip1.SD_WR:
   234  		sysHow = socketapi.SHUT_RD | socketapi.SHUT_WR
   235  	case wasip1.SD_RD:
   236  		sysHow = socketapi.SHUT_RD
   237  	case wasip1.SD_WR:
   238  		sysHow = socketapi.SHUT_WR
   239  	default:
   240  		return sys.EINVAL
   241  	}
   242  
   243  	// TODO: Map this instead of relying on syscall symbols.
   244  	return conn.Shutdown(sysHow)
   245  }