@@ -1098,12 +1098,12 @@ use std::net::SocketAddr;
10981098use std:: sync:: Mutex ;
10991099
11001100enum HostSocket {
1101- Socket ( Socket ) ,
1101+ Socket ( Socket , i32 ) ,
11021102}
11031103
11041104struct SocketTable {
1105- sockets : HashMap < u32 , HostSocket > ,
1106- next_id : u32 ,
1105+ sockets : HashMap < u64 , HostSocket > ,
1106+ next_id : u64 ,
11071107}
11081108
11091109impl SocketTable {
@@ -1114,26 +1114,32 @@ impl SocketTable {
11141114 }
11151115 }
11161116
1117- fn insert ( & mut self , sock : HostSocket ) -> u32 {
1117+ fn insert ( & mut self , sock : HostSocket ) -> u64 {
11181118 let id = self . next_id ;
11191119 self . next_id += 1 ;
11201120 self . sockets . insert ( id, sock) ;
11211121 id
11221122 }
11231123
1124- fn get ( & self , fd : u32 ) -> Result < & HostSocket > {
1124+ fn get ( & self , fd : u64 ) -> Result < & HostSocket > {
11251125 self . sockets
11261126 . get ( & fd)
11271127 . ok_or_else ( || anyhow ! ( "bad_fd: {}" , fd) )
11281128 }
11291129
1130- fn get_socket ( & self , fd : u32 ) -> Result < & Socket > {
1130+ fn get_socket ( & self , fd : u64 ) -> Result < & Socket > {
11311131 match self . get ( fd) ? {
1132- HostSocket :: Socket ( s) => Ok ( s) ,
1132+ HostSocket :: Socket ( s, _ ) => Ok ( s) ,
11331133 }
11341134 }
11351135
1136- fn remove ( & mut self , fd : u32 ) -> Result < ( ) > {
1136+ fn get_sock_type ( & self , fd : u64 ) -> Result < i32 > {
1137+ match self . get ( fd) ? {
1138+ HostSocket :: Socket ( _, t) => Ok ( * t) ,
1139+ }
1140+ }
1141+
1142+ fn remove ( & mut self , fd : u64 ) -> Result < ( ) > {
11371143 self . sockets
11381144 . remove ( & fd)
11391145 . map ( |_| ( ) )
@@ -1196,15 +1202,18 @@ fn register_net_tools(
11961202 Some ( Protocol :: from ( protocol) )
11971203 } ;
11981204 let sock = Socket :: new ( domain, stype, proto) ?;
1199- let fd = t. lock ( ) . unwrap ( ) . insert ( HostSocket :: Socket ( sock) ) ;
1205+ let fd = t
1206+ . lock ( )
1207+ . unwrap ( )
1208+ . insert ( HostSocket :: Socket ( sock, sock_type) ) ;
12001209 Ok ( json ! ( { "fd" : fd } ) )
12011210 } ) ;
12021211
12031212 // net_connect
12041213 let t = table. clone ( ) ;
12051214 let pol = policy. clone ( ) ;
12061215 tools. register ( "net_connect" , move |args| {
1207- let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ? as u32 ;
1216+ let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ?;
12081217 let addr = parse_sockaddr ( & args) ?;
12091218 pol. check ( & addr) ?;
12101219 let sa: SockAddr = addr. into ( ) ;
@@ -1218,7 +1227,7 @@ fn register_net_tools(
12181227 let t = table. clone ( ) ;
12191228 let lp = listen_ports. cloned ( ) . map ( Arc :: new) ;
12201229 tools. register ( "net_bind" , move |args| {
1221- let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ? as u32 ;
1230+ let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ?;
12221231 let addr = parse_sockaddr ( & args) ?;
12231232 match lp. as_ref ( ) {
12241233 Some ( ports) => ports. check ( addr. port ( ) ) ?,
@@ -1234,7 +1243,7 @@ fn register_net_tools(
12341243 // net_listen
12351244 let t = table. clone ( ) ;
12361245 tools. register ( "net_listen" , move |args| {
1237- let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ? as u32 ;
1246+ let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ?;
12381247 let backlog = args[ "backlog" ] . as_i64 ( ) . unwrap_or ( 128 ) as i32 ;
12391248 let tbl = t. lock ( ) . unwrap ( ) ;
12401249 let sock = tbl. get_socket ( fd) ?;
@@ -1245,14 +1254,19 @@ fn register_net_tools(
12451254 // net_accept
12461255 let t = table. clone ( ) ;
12471256 tools. register ( "net_accept" , move |args| {
1248- let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ? as u32 ;
1249- let ( new_sock, peer) = {
1257+ let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ?;
1258+ let ( new_sock, peer, parent_type ) = {
12501259 let tbl = t. lock ( ) . unwrap ( ) ;
12511260 let sock = tbl. get_socket ( fd) ?;
1252- sock. accept ( ) ?
1261+ let ( s, p) = sock. accept ( ) ?;
1262+ let st = tbl. get_sock_type ( fd) ?;
1263+ ( s, p, st)
12531264 } ;
12541265 let peer_addr: Option < SocketAddr > = peer. as_socket ( ) ;
1255- let new_fd = t. lock ( ) . unwrap ( ) . insert ( HostSocket :: Socket ( new_sock) ) ;
1266+ let new_fd = t
1267+ . lock ( )
1268+ . unwrap ( )
1269+ . insert ( HostSocket :: Socket ( new_sock, parent_type) ) ;
12561270 let mut resp = json ! ( { "fd" : new_fd } ) ;
12571271 if let Some ( pa) = peer_addr {
12581272 resp[ "addr" ] = json ! ( pa. ip( ) . to_string( ) ) ;
@@ -1264,7 +1278,7 @@ fn register_net_tools(
12641278 // net_send
12651279 let t = table. clone ( ) ;
12661280 tools. register ( "net_send" , move |args| {
1267- let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ? as u32 ;
1281+ let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ?;
12681282 let data_b64 = args[ "data" ]
12691283 . as_str ( )
12701284 . ok_or_else ( || anyhow ! ( "missing 'data'" ) ) ?;
@@ -1281,7 +1295,7 @@ fn register_net_tools(
12811295 let t = table. clone ( ) ;
12821296 let pol = policy. clone ( ) ;
12831297 tools. register ( "net_sendto" , move |args| {
1284- let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ? as u32 ;
1298+ let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ?;
12851299 let data_b64 = args[ "data" ]
12861300 . as_str ( )
12871301 . ok_or_else ( || anyhow ! ( "missing 'data'" ) ) ?;
@@ -1300,7 +1314,7 @@ fn register_net_tools(
13001314 // net_recv (alias for net_recvfrom with no addr returned for stream)
13011315 let t = table. clone ( ) ;
13021316 tools. register ( "net_recv" , move |args| {
1303- let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ? as u32 ;
1317+ let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ?;
13041318 let len = args[ "len" ] . as_u64 ( ) . unwrap_or ( 4096 ) as usize ;
13051319 let mut buf = vec ! [ std:: mem:: MaybeUninit :: uninit( ) ; len. min( 65536 ) ] ;
13061320 let tbl = t. lock ( ) . unwrap ( ) ;
@@ -1317,7 +1331,7 @@ fn register_net_tools(
13171331 // net_recvfrom
13181332 let t = table. clone ( ) ;
13191333 tools. register ( "net_recvfrom" , move |args| {
1320- let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ? as u32 ;
1334+ let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ?;
13211335 let len = args[ "len" ] . as_u64 ( ) . unwrap_or ( 4096 ) as usize ;
13221336 let mut buf = vec ! [ 0u8 ; len. min( 65536 ) ] ;
13231337
@@ -1342,15 +1356,15 @@ fn register_net_tools(
13421356 // net_close
13431357 let t = table. clone ( ) ;
13441358 tools. register ( "net_close" , move |args| {
1345- let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ? as u32 ;
1359+ let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ?;
13461360 t. lock ( ) . unwrap ( ) . remove ( fd) ?;
13471361 Ok ( json ! ( { } ) )
13481362 } ) ;
13491363
13501364 // net_shutdown
13511365 let t = table. clone ( ) ;
13521366 tools. register ( "net_shutdown" , move |args| {
1353- let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ? as u32 ;
1367+ let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ?;
13541368 let how = args[ "how" ] . as_i64 ( ) . unwrap_or ( 2 ) as i32 ;
13551369 let shutdown = match how {
13561370 0 => std:: net:: Shutdown :: Read ,
@@ -1366,7 +1380,7 @@ fn register_net_tools(
13661380 // net_setsockopt
13671381 let t = table. clone ( ) ;
13681382 tools. register ( "net_setsockopt" , move |args| {
1369- let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ? as u32 ;
1383+ let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ?;
13701384 let level = args[ "level" ] . as_i64 ( ) . unwrap_or ( 0 ) as i32 ;
13711385 let optname = args[ "optname" ] . as_i64 ( ) . unwrap_or ( 0 ) as i32 ;
13721386 let value = args[ "value" ] . as_i64 ( ) . unwrap_or ( 0 ) as i32 ;
@@ -1390,14 +1404,14 @@ fn register_net_tools(
13901404 // net_getsockopt
13911405 let t = table. clone ( ) ;
13921406 tools. register ( "net_getsockopt" , move |args| {
1393- let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ? as u32 ;
1407+ let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ?;
13941408 let level = args[ "level" ] . as_i64 ( ) . unwrap_or ( 0 ) as i32 ;
13951409 let optname = args[ "optname" ] . as_i64 ( ) . unwrap_or ( 0 ) as i32 ;
13961410 let tbl = t. lock ( ) . unwrap ( ) ;
13971411 let sock = tbl. get_socket ( fd) ?;
13981412 let val: i32 = if level == 1 && optname == 3 {
1399- // SOL_SOCKET + SO_TYPE — all our sockets are SOCK_STREAM
1400- 1
1413+ // SOL_SOCKET + SO_TYPE — return the actual socket type
1414+ tbl . get_sock_type ( fd ) ?
14011415 } else if level == 1 && optname == 2 {
14021416 sock. reuse_address ( ) ? as i32
14031417 } else if level == 6 && optname == 1 {
@@ -1411,7 +1425,7 @@ fn register_net_tools(
14111425 // net_getpeername
14121426 let t = table. clone ( ) ;
14131427 tools. register ( "net_getpeername" , move |args| {
1414- let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ? as u32 ;
1428+ let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ?;
14151429 let tbl = t. lock ( ) . unwrap ( ) ;
14161430 let sock = tbl. get_socket ( fd) ?;
14171431 let peer = sock. peer_addr ( ) ?;
@@ -1425,7 +1439,7 @@ fn register_net_tools(
14251439 // net_getsockname
14261440 let t = table. clone ( ) ;
14271441 tools. register ( "net_getsockname" , move |args| {
1428- let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ? as u32 ;
1442+ let fd = args[ "fd" ] . as_u64 ( ) . ok_or_else ( || anyhow ! ( "missing 'fd'" ) ) ?;
14291443 let tbl = t. lock ( ) . unwrap ( ) ;
14301444 let sock = tbl. get_socket ( fd) ?;
14311445 let local = sock. local_addr ( ) ?;
@@ -2267,7 +2281,11 @@ pub fn run_vm_capture_output(
22672281 let setup_time = setup_start. elapsed ( ) ;
22682282
22692283 // Redirect stderr to a temp file before the call phase
2270- let capture_file = std:: env:: temp_dir ( ) . join ( format ! ( "hl-capture-{}" , std:: process:: id( ) ) ) ;
2284+ let capture_file = std:: env:: temp_dir ( ) . join ( format ! (
2285+ "hl-capture-{}-{:?}" ,
2286+ std:: process:: id( ) ,
2287+ std:: thread:: current( ) . id( )
2288+ ) ) ;
22712289 let capture = stderr_capture:: Capture :: redirect_to_file ( & capture_file) ?;
22722290
22732291 // Phase 2: restore + call — application runs and produces output
@@ -2986,14 +3004,11 @@ mod tests {
29863004
29873005 #[ test]
29883006 fn test_fs_read_bytes_capped ( ) {
2989- // Request a huge len (well above MAX_FS_READ) on a small file.
2990- // Without the cap this would try to allocate terabytes and OOM.
29913007 let root = tmpdir ( "readcap" ) ;
29923008 fs:: write ( root. join ( "small.bin" ) , b"hello" ) . unwrap ( ) ;
29933009 let mut reg = ToolRegistry :: new ( ) ;
29943010 FsSandbox :: new ( & root) . unwrap ( ) . register ( & mut reg) ;
29953011
2996- // Ask for 1 TiB — the cap should silently clamp to MAX_FS_READ.
29973012 let req = br#"{"name":"fs_read_bytes","args":{"path":"small.bin","len":1099511627776}}"# ;
29983013 let resp = reg. dispatch ( req) ;
29993014 let s = std:: str:: from_utf8 ( & resp) . unwrap ( ) ;
@@ -3003,14 +3018,8 @@ mod tests {
30033018
30043019 #[ test]
30053020 fn test_sleep_capped ( ) {
3006- // Verify the cap constant and that sleeping with a huge value
3007- // completes quickly (the cap brings it down to 60 s max, but we
3008- // pass 0 to keep the test instant — the important thing is
3009- // confirming the cap constant exists and has the right value).
30103021 assert_eq ! ( MAX_SLEEP_NS , 60_000_000_000 ) ;
30113022
3012- // Dispatch a sleep with ns=0 through the real handler to confirm
3013- // the code path works.
30143023 let mut tools = ToolRegistry :: new ( ) ;
30153024 let exit_code = Arc :: new ( AtomicI32 :: new ( 0 ) ) ;
30163025 register_internal_tools ( & mut tools, & exit_code, None , None ) ;
@@ -3020,4 +3029,27 @@ mod tests {
30203029 let s = std:: str:: from_utf8 ( & resp) . unwrap ( ) ;
30213030 assert ! ( !s. contains( "\" error\" " ) , "sleep(0) should succeed: {s}" ) ;
30223031 }
3032+
3033+ #[ test]
3034+ fn net_getsockopt_returns_correct_type_for_dgram ( ) {
3035+ let mut reg = ToolRegistry :: new ( ) ;
3036+ let policy = NetworkPolicy :: AllowAll ;
3037+ register_net_tools ( & mut reg, & policy, None ) ;
3038+
3039+ let req = br#"{"name":"net_socket","args":{"family":2,"type":2}}"# ;
3040+ let resp = std:: str:: from_utf8 ( & reg. dispatch ( req) ) . unwrap ( ) . to_string ( ) ;
3041+ let v: serde_json:: Value = serde_json:: from_str ( & resp) . unwrap ( ) ;
3042+ let fd = v[ "result" ] [ "fd" ] . as_u64 ( ) . unwrap ( ) ;
3043+
3044+ let req =
3045+ format ! ( r#"{{"name":"net_getsockopt","args":{{"fd":{fd},"level":1,"optname":3}}}}"# ) ;
3046+ let resp = std:: str:: from_utf8 ( & reg. dispatch ( req. as_bytes ( ) ) )
3047+ . unwrap ( )
3048+ . to_string ( ) ;
3049+ let v: serde_json:: Value = serde_json:: from_str ( & resp) . unwrap ( ) ;
3050+ assert_eq ! (
3051+ v[ "result" ] [ "value" ] , 2 ,
3052+ "SO_TYPE should return 2 (DGRAM), got: {resp}"
3053+ ) ;
3054+ }
30233055}
0 commit comments