github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/imports/wasi_snapshot_preview1/sock.go (about)

     1  package wasi_snapshot_preview1
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/tetratelabs/wazero/api"
     7  	"github.com/tetratelabs/wazero/experimental/sys"
     8  	socketapi "github.com/tetratelabs/wazero/internal/sock"
     9  	"github.com/tetratelabs/wazero/internal/sysfs"
    10  	"github.com/tetratelabs/wazero/internal/wasip1"
    11  	"github.com/tetratelabs/wazero/internal/wasm"
    12  )
    13  
    14  // sockAccept is the WASI function named SockAcceptName which accepts a new
    15  // incoming connection.
    16  //
    17  // See: https://github.com/WebAssembly/WASI/blob/0ba0c5e2e37625ca5a6d3e4255a998dfaa3efc52/phases/snapshot/docs.md#sock_accept
    18  // and https://github.com/WebAssembly/WASI/pull/458
    19  var sockAccept = newHostFunc(
    20  	wasip1.SockAcceptName,
    21  	sockAcceptFn,
    22  	[]wasm.ValueType{i32, i32, i32},
    23  	"fd", "flags", "result.fd",
    24  )
    25  
    26  func sockAcceptFn(_ context.Context, mod api.Module, params []uint64) (errno sys.Errno) {
    27  	mem := mod.Memory()
    28  	fsc := mod.(*wasm.ModuleInstance).Sys.FS()
    29  
    30  	fd := int32(params[0])
    31  	flags := uint32(params[1])
    32  	resultFd := uint32(params[2])
    33  	nonblock := flags&uint32(wasip1.FD_NONBLOCK) != 0
    34  
    35  	var connFD int32
    36  	if connFD, errno = fsc.SockAccept(fd, nonblock); errno == 0 {
    37  		mem.WriteUint32Le(resultFd, uint32(connFD))
    38  	}
    39  	return
    40  }
    41  
    42  // sockRecv is the WASI function named SockRecvName which receives a
    43  // message from a socket.
    44  //
    45  // 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
    46  var sockRecv = newHostFunc(
    47  	wasip1.SockRecvName,
    48  	sockRecvFn,
    49  	[]wasm.ValueType{i32, i32, i32, i32, i32, i32},
    50  	"fd", "ri_data", "ri_data_len", "ri_flags", "result.ro_datalen", "result.ro_flags",
    51  )
    52  
    53  func sockRecvFn(_ context.Context, mod api.Module, params []uint64) sys.Errno {
    54  	mem := mod.Memory()
    55  	fsc := mod.(*wasm.ModuleInstance).Sys.FS()
    56  
    57  	fd := int32(params[0])
    58  	riData := uint32(params[1])
    59  	riDataCount := uint32(params[2])
    60  	riFlags := uint8(params[3])
    61  	resultRoDatalen := uint32(params[4])
    62  	resultRoFlags := uint32(params[5])
    63  
    64  	var conn socketapi.TCPConn
    65  	if e, ok := fsc.LookupFile(fd); !ok {
    66  		return sys.EBADF // Not open
    67  	} else if conn, ok = e.File.(socketapi.TCPConn); !ok {
    68  		return sys.EBADF // Not a conn
    69  	}
    70  
    71  	if riFlags & ^(wasip1.RI_RECV_PEEK|wasip1.RI_RECV_WAITALL) != 0 {
    72  		return sys.ENOTSUP
    73  	}
    74  
    75  	if riFlags&wasip1.RI_RECV_PEEK != 0 {
    76  		// Each record in riData is of the form:
    77  		// type iovec struct { buf *uint8; bufLen uint32 }
    78  		// This means that the first `uint32` is a `buf *uint8`.
    79  		firstIovecBufAddr, ok := mem.ReadUint32Le(riData)
    80  		if !ok {
    81  			return sys.EINVAL
    82  		}
    83  		// Read bufLen
    84  		firstIovecBufLen, ok := mem.ReadUint32Le(riData + 4)
    85  		if !ok {
    86  			return sys.EINVAL
    87  		}
    88  		firstIovecBuf, ok := mem.Read(firstIovecBufAddr, firstIovecBufLen)
    89  		if !ok {
    90  			return sys.EINVAL
    91  		}
    92  		n, err := conn.Recvfrom(firstIovecBuf, sysfs.MSG_PEEK)
    93  		if err != 0 {
    94  			return err
    95  		}
    96  		mem.WriteUint32Le(resultRoDatalen, uint32(n))
    97  		mem.WriteUint16Le(resultRoFlags, 0)
    98  		return 0
    99  	}
   100  
   101  	// If riFlags&wasip1.RECV_WAITALL != 0 then we should
   102  	// do a blocking operation until all data has been retrieved;
   103  	// otherwise we are able to return earlier.
   104  	// For simplicity, we currently wait all regardless the flag.
   105  	bufSize, errno := readv(mem, riData, riDataCount, conn.Read)
   106  	if errno != 0 {
   107  		return errno
   108  	}
   109  	mem.WriteUint32Le(resultRoDatalen, bufSize)
   110  	mem.WriteUint16Le(resultRoFlags, 0)
   111  	return 0
   112  }
   113  
   114  // sockSend is the WASI function named SockSendName which sends a message
   115  // on a socket.
   116  //
   117  // 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
   118  var sockSend = newHostFunc(
   119  	wasip1.SockSendName,
   120  	sockSendFn,
   121  	[]wasm.ValueType{i32, i32, i32, i32, i32},
   122  	"fd", "si_data", "si_data_len", "si_flags", "result.so_datalen",
   123  )
   124  
   125  func sockSendFn(_ context.Context, mod api.Module, params []uint64) sys.Errno {
   126  	mem := mod.Memory()
   127  	fsc := mod.(*wasm.ModuleInstance).Sys.FS()
   128  
   129  	fd := int32(params[0])
   130  	siData := uint32(params[1])
   131  	siDataCount := uint32(params[2])
   132  	siFlags := uint32(params[3])
   133  	resultSoDatalen := uint32(params[4])
   134  
   135  	if siFlags != 0 {
   136  		return sys.ENOTSUP
   137  	}
   138  
   139  	var conn socketapi.TCPConn
   140  	if e, ok := fsc.LookupFile(fd); !ok {
   141  		return sys.EBADF // Not open
   142  	} else if conn, ok = e.File.(socketapi.TCPConn); !ok {
   143  		return sys.EBADF // Not a conn
   144  	}
   145  
   146  	bufSize, errno := writev(mem, siData, siDataCount, conn.Write)
   147  	if errno != 0 {
   148  		return errno
   149  	}
   150  	mem.WriteUint32Le(resultSoDatalen, bufSize)
   151  	return 0
   152  }
   153  
   154  // sockShutdown is the WASI function named SockShutdownName which shuts
   155  // down socket send and receive channels.
   156  //
   157  // See: https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-sock_shutdownfd-fd-how-sdflags---errno
   158  var sockShutdown = newHostFunc(wasip1.SockShutdownName, sockShutdownFn, []wasm.ValueType{i32, i32}, "fd", "how")
   159  
   160  func sockShutdownFn(_ context.Context, mod api.Module, params []uint64) sys.Errno {
   161  	fsc := mod.(*wasm.ModuleInstance).Sys.FS()
   162  
   163  	fd := int32(params[0])
   164  	how := uint8(params[1])
   165  
   166  	var conn socketapi.TCPConn
   167  	if e, ok := fsc.LookupFile(fd); !ok {
   168  		return sys.EBADF // Not open
   169  	} else if conn, ok = e.File.(socketapi.TCPConn); !ok {
   170  		return sys.EBADF // Not a conn
   171  	}
   172  
   173  	sysHow := 0
   174  
   175  	switch how {
   176  	case wasip1.SD_RD | wasip1.SD_WR:
   177  		sysHow = socketapi.SHUT_RD | socketapi.SHUT_WR
   178  	case wasip1.SD_RD:
   179  		sysHow = socketapi.SHUT_RD
   180  	case wasip1.SD_WR:
   181  		sysHow = socketapi.SHUT_WR
   182  	default:
   183  		return sys.EINVAL
   184  	}
   185  
   186  	// TODO: Map this instead of relying on syscall symbols.
   187  	return conn.Shutdown(sysHow)
   188  }