This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: socket.setsockopt() is still broken for multicast TTL and Loop options
Type: behavior Stage:
Components: Library (Lib) Versions: Python 3.4, Python 2.7
process
Status: closed Resolution: wont fix
Dependencies: Superseder:
Assigned To: Nosy List: benjamin.peterson, tamentis
Priority: normal Keywords:

Created on 2014-12-29 13:31 by tamentis, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Messages (6)
msg233174 - (view) Author: Bertrand Janin (tamentis) * Date: 2014-12-29 13:31
Since I can't re-open issue 3372, I'm opening a new issue.  socket.setsockopt() still sets an optlen of '4' in the setsockopt() system call for options IP_MULTICAST_TTL and IP_MULTICAST_LOOP.  On OpenBSD, this causes the kernel to hit an error condition and set errno 22 when these options are set:

    socket.error: (22, 'Invalid argument')

According to both FreeBSD and OpenBSD manual pages (see e.g. http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man4/ip.4), these fields are in fact both 8 bit unsigned, and the manuals suggest the following:

    u_char ttl;	/* range: 0 to 255, default = 1 */ 
    setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));

The following updated patch for branch "2.7" passes a shorter "optlen" for certain Multicast options, it was tested on OpenBSD, Linux and OSX and was initially proposed by niallo:

diff -r 88de50c1696b Modules/socketmodule.c
--- a/Modules/socketmodule.c    Sun Dec 28 18:51:25 2014 +0200
+++ b/Modules/socketmodule.c    Mon Dec 29 08:27:24 2014 -0500
@@ -1879,26 +1879,29 @@
 static PyObject *
 sock_setsockopt(PySocketSockObject *s, PyObject *args)
 {
     int level;
     int optname;
     int res;
     char *buf;
     int buflen;
     int flag;
 
     if (PyArg_ParseTuple(args, "iii:setsockopt",
                          &level, &optname, &flag)) {
+        buflen = sizeof flag;
+        /* Multicast options take shorter arguments */
+        if (optname == IP_MULTICAST_TTL || optname == IP_MULTICAST_LOOP)
+            buflen = sizeof(u_char);
         buf = (char *) &flag;
-        buflen = sizeof flag;
     }
     else {
         PyErr_Clear();
         if (!PyArg_ParseTuple(args, "iis#:setsockopt",
                               &level, &optname, &buf, &buflen))
             return NULL;
     }
     res = setsockopt(s->sock_fd, level, optname, (void *)buf, buflen);
     if (res < 0)
         return s->errorhandler();
     Py_INCREF(Py_None);
     return Py_None;
msg233180 - (view) Author: Benjamin Peterson (benjamin.peterson) * (Python committer) Date: 2014-12-29 18:17
"Just" use the extended signature of the setsockopt:
    mysocket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, struct.pack("B", desired_ttl))
msg233223 - (view) Author: Bertrand Janin (tamentis) * Date: 2014-12-30 23:23
I don't really need it in my own software, I was trying to use https://github.com/SoCo/SoCo/ and couldn't get it working on OpenBSD.  I'm sure this is a portability problem on a number of library using Multicast.  Do you see something wrong with the patch?
msg233225 - (view) Author: Benjamin Peterson (benjamin.peterson) * (Python committer) Date: 2014-12-31 00:25
We don't really have a precedent for adjusting the size automatically when integers are passed to setsockopt. Anyway, your patch will be wrong for big-endian systems.
msg233271 - (view) Author: Bertrand Janin (tamentis) * Date: 2014-12-31 19:36
Good point, I updated the diff with a better cast to avoid endianness issues (tested on MIPS64) and added an OpenBSD #ifdef, is that more acceptable?

diff -r 88de50c1696b Modules/socketmodule.c
--- a/Modules/socketmodule.c	Sun Dec 28 18:51:25 2014 +0200
+++ b/Modules/socketmodule.c	Wed Dec 31 14:25:55 2014 -0500
@@ -1881,24 +1881,31 @@
 {
     int level;
     int optname;
     int res;
     char *buf;
     int buflen;
     int flag;
 
     if (PyArg_ParseTuple(args, "iii:setsockopt",
                          &level, &optname, &flag)) {
         buf = (char *) &flag;
         buflen = sizeof flag;
+#if defined(__OpenBSD__)
+        /* Multicast options take shorter arguments */
+        if (optname == IP_MULTICAST_TTL || optname == IP_MULTICAST_LOOP) {
+            buflen = sizeof(u_char);
+            *buf = (u_char)flag;
+        }
+#endif
     }
     else {
         PyErr_Clear();
         if (!PyArg_ParseTuple(args, "iis#:setsockopt",
                               &level, &optname, &buf, &buflen))
             return NULL;
     }
     res = setsockopt(s->sock_fd, level, optname, (void *)buf, buflen);
     if (res < 0)
         return s->errorhandler();
     Py_INCREF(Py_None);
     return Py_None;
msg233329 - (view) Author: Benjamin Peterson (benjamin.peterson) * (Python committer) Date: 2015-01-02 21:51
Application code should pass the correct length of value.
History
Date User Action Args
2022-04-11 14:58:11adminsetgithub: 67316
2015-01-02 21:51:44benjamin.petersonsetstatus: open -> closed
resolution: wont fix
messages: + msg233329
2014-12-31 19:36:24tamentissetmessages: + msg233271
2014-12-31 00:25:06benjamin.petersonsetmessages: + msg233225
2014-12-30 23:23:04tamentissetmessages: + msg233223
2014-12-29 18:17:08benjamin.petersonsetnosy: + benjamin.peterson
messages: + msg233180
2014-12-29 13:31:07tamentiscreate