Repository: denoland/deno
Pull request: fix(ext/node): implement dgram socket setMulticastTTL

Ref https://github.com/ipfs/helia/issues/73#issuecomment-2864091259

Changed files:
filename: ext/net/01_net.js
patch: @@ -380,6 +380,7 @@ class Listener {
 
 const _setBroadcast = Symbol("setBroadcast");
 const _dropMembership = Symbol("dropMembership");
+const _setMulticastTTL = Symbol("setMulticastTTL");
 
 function setDatagramBroadcast(conn, broadcast) {
   return conn[_setBroadcast](broadcast);
@@ -389,6 +390,10 @@ function dropMembership(conn, v6, addr, multiInterface) {
   return conn[_dropMembership](v6, addr, multiInterface);
 }
 
+function setMulticastTTL(conn, ttl) {
+  return conn[_setMulticastTTL](ttl);
+}
+
 class DatagramConn {
   #rid = 0;
   #addr = null;
@@ -417,6 +422,10 @@ class DatagramConn {
     return op_net_leave_multi_v4_udp(this.#rid, addr, multiInterface);
   }
 
+  [_setMulticastTTL](ttl) {
+    return op_net_set_multi_ttl_udp(this.#rid, ttl);
+  }
+
   async joinMulticastV4(addr, multiInterface) {
     await op_net_join_multi_v4_udp(
       this.#rid,
@@ -714,6 +723,7 @@ export {
   listenOptionApiName,
   resolveDns,
   setDatagramBroadcast,
+  setMulticastTTL,
   TcpConn,
   UnixConn,
   UpgradedConn,

filename: ext/node/polyfills/internal_binding/udp_wrap.ts
patch: @@ -364,8 +364,23 @@ export class UDP extends HandleWrap {
     notImplemented("udp.UDP.prototype.setMulticastLoopback");
   }
 
-  setMulticastTTL(_ttl: number): number {
-    notImplemented("udp.UDP.prototype.setMulticastTTL");
+  setMulticastTTL(ttl: number): number {
+    if (ttl < 1 || ttl > 255) {
+      return codeMap.get("EINVAL")!;
+    }
+
+    if (!this.#listener) {
+      return codeMap.get("EBADF")!;
+    }
+
+    try {
+      if (this.#family === "IPv4") {
+        net.setMulticastTTL(this.#listener, ttl);
+      }
+      return 0;
+    } catch {
+      return codeMap.get("EINVAL")!;
+    }
   }
 
   setTTL(_ttl: number): number {

filename: tests/node_compat/config.jsonc
patch: @@ -429,6 +429,7 @@
       "test-dgram-listen-after-bind.js",
       "test-dgram-membership.js",
       "test-dgram-msgsize.js",
+      "test-dgram-multicast-setTTL.js",
       "test-dgram-oob-buffer.js",
       "test-dgram-recv-error.js",
       "test-dgram-ref.js",

filename: tests/node_compat/runner/TODO.md
patch: @@ -1,7 +1,7 @@
 <!-- deno-fmt-ignore-file -->
 # Remaining Node Tests
 
-1166 tests out of 3993 have been ported from Node 23.9.0 (29.20% ported, 71.32% remaining).
+1167 tests out of 3993 have been ported from Node 23.9.0 (29.23% ported, 71.30% remaining).
 
 NOTE: This file should not be manually edited. Please edit `tests/node_compat/config.json` and run `deno task setup` in `tests/node_compat/runner` dir instead.
 
@@ -616,7 +616,6 @@ NOTE: This file should not be manually edited. Please edit `tests/node_compat/co
 - [parallel/test-dgram-exclusive-implicit-bind.js](https://github.com/nodejs/node/tree/v23.9.0/test/parallel/test-dgram-exclusive-implicit-bind.js)
 - [parallel/test-dgram-multicast-loopback.js](https://github.com/nodejs/node/tree/v23.9.0/test/parallel/test-dgram-multicast-loopback.js)
 - [parallel/test-dgram-multicast-set-interface.js](https://github.com/nodejs/node/tree/v23.9.0/test/parallel/test-dgram-multicast-set-interface.js)
-- [parallel/test-dgram-multicast-setTTL.js](https://github.com/nodejs/node/tree/v23.9.0/test/parallel/test-dgram-multicast-setTTL.js)
 - [parallel/test-dgram-reuseport.js](https://github.com/nodejs/node/tree/v23.9.0/test/parallel/test-dgram-reuseport.js)
 - [parallel/test-dgram-send-address-types.js](https://github.com/nodejs/node/tree/v23.9.0/test/parallel/test-dgram-send-address-types.js)
 - [parallel/test-dgram-send-empty-array.js](https://github.com/nodejs/node/tree/v23.9.0/test/parallel/test-dgram-send-empty-array.js)

filename: tests/node_compat/test/parallel/test-dgram-multicast-setTTL.js
patch: @@ -0,0 +1,56 @@
+// deno-fmt-ignore-file
+// deno-lint-ignore-file
+
+// Copyright Joyent and Node contributors. All rights reserved. MIT license.
+// Taken from Node 23.9.0
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
+
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+'use strict';
+const common = require('../common');
+const assert = require('assert');
+const dgram = require('dgram');
+const socket = dgram.createSocket('udp4');
+
+socket.bind(0);
+socket.on('listening', common.mustCall(() => {
+  const result = socket.setMulticastTTL(16);
+  assert.strictEqual(result, 16);
+
+  // Try to set an invalid TTL (valid ttl is > 0 and < 256)
+  assert.throws(() => {
+    socket.setMulticastTTL(1000);
+  }, /^Error: setMulticastTTL EINVAL$/);
+
+  assert.throws(() => {
+    socket.setMulticastTTL('foo');
+  }, {
+    code: 'ERR_INVALID_ARG_TYPE',
+    name: 'TypeError',
+    message: 'The "ttl" argument must be of type number. Received type string' +
+             " ('foo')"
+  });
+
+  // Close the socket
+  socket.close();
+}));