Jean-Paul Calderone wrote:
On Tue, 09 Jun 2009 16:46:54 +0200, Kálmán Gergely <kalman.gerg...@duodecad.hu> wrote:
Hello, my name is Greg.

I've just started using python after many years of C programming, and I'm also new to the list. I wanted to clarify this first, so that maybe I will get a little less beating for my stupidity :)


Welcome!


[snip]

Browsing the net I've found a patch to the python core (http://bugs.python.org/issue1194378), dated 2005.

First of all, I would like to ask you guys, whether you know of any way of doing this FD passing magic, or that you know
of any 3rd party module / patch / anything that can do this for me.

Aside from the patch in the tracker, there are several implementations of
these APIs as third-party extension modules.


Since I'm fairly familiar with C and (not that much, but I feel the power) python, I would take the challenge of writing it, given that the above code is still somewhat usable. If all else fails I would like to have your help to guide me through
this process.


What would be great is if you could take the patch in the tracker and get
it into shape so that it is suitable for inclusion.  This would involve
three things, I think:

1. Write unit tests for the functionality (since the patch itself provides
    none)
 2. Update the patch so that it again applies cleanly against trunk
 3. Add documentation for the new APIs

Once this is done, you can get a committer to look at it and either provide
more specific feedback or apply it.

Thanks,

Jean-Paul
_______________________________________________
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: http://mail.python.org/mailman/options/python-dev/synapse%40jasmin.hu

Hello again

So, after a little cleanup I've managed to integrate the code into socketmodule.c/h. It works fine now, though I needed to add it to Lib/socket.py, otherwise it wouldn't show up in the socket module (I've searched for recvfrom and added it). I've also cleaned up the code a little, fixed some codingstyle issues (which might still exist).

Since I am not a python core developer the patch might still be in a pretty outdated state. I'd like someone to look it over and direct me to some documentation (the ones I've found so far were pretty sketchy), for it to be acceptable for inclusion. The sanity of the code is what matters to me the most. I've looked it over though and found it in a sound state, but I guess
you guys might have a different opinion about that ;)

With writing the test cases, some documentation would be nice.

I've attached the patch generated with svn diff for revision 73434, and the test code that I use to pass a file descriptor
between processes. It works just fine :).

Thanks

Kalman Gergely

Index: Lib/socket.py
===================================================================
--- Lib/socket.py	(revision 73434)
+++ Lib/socket.py	(working copy)
@@ -159,14 +159,14 @@
 # All the method names that must be delegated to either the real socket
 # object or the _closedsocket object.
 _delegate_methods = ("recv", "recvfrom", "recv_into", "recvfrom_into",
-                     "send", "sendto")
+                     "recvmsg", "send", "sendto", "sendmsg")
 
 class _closedsocket(object):
     __slots__ = []
     def _dummy(*args):
         raise error(EBADF, 'Bad file descriptor')
     # All _delegate_methods must also be initialized here.
-    send = recv = recv_into = sendto = recvfrom = recvfrom_into = _dummy
+    send = recv = recv_into = sendto = recvfrom = recvfrom_into = recvmsg = sendmsg = _dummy
     __getattr__ = _dummy
 
 # Wrapper around platform socket objects. This implements
Index: Modules/socketmodule.c
===================================================================
--- Modules/socketmodule.c	(revision 73434)
+++ Modules/socketmodule.c	(working copy)
@@ -251,6 +251,9 @@
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #endif
+#include <sys/stat.h>
+#include <unistd.h>
+#include "structmember.h"
 
 /* Generic socket object definitions and includes */
 #define PySocket_BUILDING_SOCKET
@@ -1840,6 +1843,7 @@
 	int optname;
 	int res;
 	PyObject *buf;
+	char *data;
 	socklen_t buflen = 0;
 
 #ifdef __BEOS__
@@ -1852,6 +1856,36 @@
 			      &level, &optname, &buflen))
 		return NULL;
 
+#ifdef SO_PEERCRED
+	if( level == SOL_SOCKET && optname == SO_PEERCRED ) {
+	    /* Buffer length for struct ucred, ignore parameter. */
+	    buflen = sizeof(int)*3;
+
+	    /* Allocate ucred structure and data buffer. */
+	    if( !( buf = PyType_GenericAlloc((PyTypeObject*)&ucred_type,1) ) )
+		return NULL;
+	    if( !( data = PyMem_Malloc(buflen) ) ) {
+		Py_DECREF(buf);
+		return NULL;
+	    }
+
+	    /* Use getsockopt to retrieve data. */
+	    if( ( res = getsockopt(s->sock_fd,level,optname,data,&buflen) ) < 
+		0 ) {
+		Py_DECREF(buf);
+		return s->errorhandler();
+	    }
+
+	    /* Write it out to object. */
+	    ((PySocketUcredObject*)buf)->uid = ((int*)data)[1];
+	    ((PySocketUcredObject*)buf)->gid = ((int*)data)[2];
+	    ((PySocketUcredObject*)buf)->pid = ((int*)data)[0];
+
+	    /* Free data buffer and return. */
+	    PyMem_Free(data);
+	    return buf;
+	}
+#endif
 	if (buflen == 0) {
 		int flag = 0;
 		socklen_t flagsize = sizeof flag;
@@ -2871,6 +2905,455 @@
 
 #endif
 
+/* This is defined as the normal OS-buffer in the corresponding RFC. */
+#define RECVMSG_MAX_ABUFFER_LEN 10240
+
+static PyObject *
+sock_recvmsg(PySocketSockObject *s, PyObject *args)
+{
+	int res = -1, cmsgdatalen, flags = 0, timeout, recvlen, recvanc = 1, i = 0;
+	int fd, fdtype /* , fdfamily, fdproto for sockets, TODO... */;
+	struct iovec iov;
+	struct msghdr msg;
+	struct cmsghdr *cmsgh;
+	struct stat fdstat;
+	char *fdmode;
+	PyObject *rv = NULL, *rvitem = NULL, *addrarg = NULL, *curitem = NULL,
+		*newitem = NULL;
+
+	/* Initialize iov and msg buffer. */
+	memset((void*)&iov, 0, sizeof(iov));
+	memset((void*)&msg, 0, sizeof(msg));
+
+	/* Parse arguments. Argument are: recv length, address object, flags. */
+	if(!PyArg_ParseTuple(args, "i|Oii:recvmsg",
+	   &recvlen, &addrarg, &recvanc, &flags))
+		goto error;
+
+	/* Create IO Vector from buffer length. */
+	if(recvlen <= 0) {
+		PyErr_SetString(PyExc_ValueError,
+				"receive length must be >= 0.");
+		goto error;
+	}
+
+	iov.iov_base = PyMem_Malloc(recvlen);
+	if(!iov.iov_base) {
+		PyErr_SetString(PyExc_MemoryError,
+				"cannot allocate memory for receive buffer.");
+		goto error;
+	}
+	iov.iov_len = recvlen;
+
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+
+	/* Parse name parameter. */
+	if(addrarg && addrarg != Py_None)
+		if(!getsockaddrarg(s, addrarg,
+		   (struct sockaddr**)&msg.msg_name,
+		   (int*)&msg.msg_namelen))
+			goto error;
+
+	/* Create buffer for control messages. */
+	if(recvanc) {
+		msg.msg_control = PyMem_Malloc(RECVMSG_MAX_ABUFFER_LEN);
+		if(!msg.msg_control) {
+			PyErr_SetString(PyExc_MemoryError,
+				"cannot allocate memory for ancmsg buffer.");
+			goto error;
+		}
+		msg.msg_controllen = RECVMSG_MAX_ABUFFER_LEN;
+	}
+
+	/* Call function, allowing threads. */
+	Py_BEGIN_ALLOW_THREADS;
+	timeout = internal_select(s, 0);
+	if(!timeout)
+		res = recvmsg(s->sock_fd, &msg,flags);
+	Py_END_ALLOW_THREADS;
+
+	/* Return value was -1, a socket error. */
+	if (timeout) {
+		PyErr_SetString(socket_timeout, "timed out");
+		goto error;
+	} else if(res == -1) {
+		s->errorhandler();
+		goto error;
+	}
+
+	/* Initialize cmsg header pointer. */
+	cmsgh = CMSG_FIRSTHDR(&msg);
+
+	/* Create new return value. */
+	if(!(rv = PyTuple_New(3)))
+		goto cmsg_error;
+
+	/* Create new message string. */
+	if(!(rvitem = PyString_FromStringAndSize((const char*)iov.iov_base,
+	   res)))
+		goto cmsg_error;
+
+	PyTuple_SET_ITEM(rv, 0, rvitem);
+
+	/* Pack control messages. */
+	if(!(rvitem = PyList_New(0)))
+		goto cmsg_error;
+
+	while(cmsgh) {
+		/* Check for data content of cmsg struct. */
+		if((cmsgdatalen = cmsgh->cmsg_len - sizeof(struct cmsghdr))) {
+			/* Did we receive a SCM_RIGHTS message? */
+			if(cmsgh->cmsg_level == SOL_SOCKET &&
+			   cmsgh->cmsg_type == SCM_RIGHTS){
+				/* Create list to store files. */
+				if(!(curitem = PyList_New(0)))
+					goto cmsg_error;
+
+				/* Loop over files and create Python objects for them. */
+				for(; i < cmsgdatalen; i += sizeof(int)) {
+					/* Get file descriptor and type. */
+					fd = *((int*)(CMSG_DATA(cmsgh) + i));
+					if(fstat(fd, &fdstat)) {
+						PyErr_SetString(PyExc_IOError,"stat on desc failed.");
+						goto cmsg_error;
+					}
+
+					/* Check for file descriptor type. */
+					if(S_ISSOCK(fdstat.st_mode)) {
+						/* Get socket parameters.
+						   TODO: Implement, currently noop to do something
+						   sensible. */
+						close(fd);
+						continue;
+					} else if(S_ISDIR(fdstat.st_mode)) {
+						/* We don't support opening directories in Python. Do
+						   something sensible, TODO, don't error for now. */
+						close(fd);
+						continue;
+					} else {
+						/* We got a file(-like descriptor). */
+						/* Get file open flags from file descriptor. */
+						if((fdtype = fcntl(fd, F_GETFL)) == -1) {
+							PyErr_SetString(PyExc_IOError,
+									"could not get flags");
+							goto cmsg_error;
+						}
+
+						/* Check for file type. */
+						if((fdtype & O_RDWR) && (fdtype & O_APPEND))
+							fdmode = "a+";
+						else if((fdtype & O_WRONLY) && (fdtype & O_APPEND))
+							fdmode = "a";
+						else if(fdtype & O_RDWR)
+							fdmode = "r+";
+						else if(fdtype & O_WRONLY)
+							fdmode = "w";
+						else	/* We got some combination which sucks... Just
+							open in reading mode, which should work. */
+							fdmode = "r";
+
+						/* Create new Python file object. */
+						if(!(newitem = PyFile_FromFile(fdopen(fd, fdmode),
+						   "<SCM_RIGHTS file>", fdmode, fclose)))
+							goto cmsg_error;
+					}
+
+					/* And append to list of file objects. */
+					if(PyList_Append(curitem,newitem)) {
+						/* This file is in a fileob already, it's closed by
+						   destroying newitem. */
+						i += sizeof(int);
+						goto cmsg_error;
+					}
+					Py_DECREF(newitem);
+					newitem = NULL;
+				} // for
+#ifdef SCM_CREDENTIALS /* Linux 2.2+ only! */
+			} else if(cmsgh->cmsg_level == SOL_SOCKET &&
+				  cmsgh->cmsg_type == SCM_CREDENTIALS) {
+				/* Allocate new ucred. */
+				if(!(curitem = PyType_GenericAlloc((PyTypeObject*)&ucred_type,
+				   1)))
+					goto cmsg_error;
+
+				/* Store data in it. */
+				((PySocketUcredObject*)curitem)->uid =
+				    ((int*)CMSG_DATA(cmsgh))[1];
+				((PySocketUcredObject*)curitem)->gid =
+				    ((int*)CMSG_DATA(cmsgh))[2];
+				((PySocketUcredObject*)curitem)->pid =
+				    ((int*)CMSG_DATA(cmsgh))[0];
+#endif
+			} else
+				/* We got something else with data. What is this? Don't parse
+				   it. */
+				curitem = Py_BuildValue("iis#", cmsgh->cmsg_level,
+						cmsgh->cmsg_type, CMSG_DATA(cmsgh),
+						cmsgdatalen);
+		} else
+			/* We got something else with no data. What is this? Don't do any
+			   more guesswork. */
+			curitem = Py_BuildValue("iis", cmsgh->cmsg_level,
+					cmsgh->cmsg_type, NULL);
+
+		if(!curitem)
+			goto cmsg_error;
+		if(PyList_Append(rvitem, curitem))
+			goto cmsg_error;
+		Py_DECREF(curitem);
+		curitem = NULL;
+
+		cmsgh = CMSG_NXTHDR(&msg, cmsgh);
+		i = 0;
+	} // while
+
+	PyTuple_SET_ITEM(rv, 1, rvitem);
+
+	/* Append flags to return value. */
+	if(!(rvitem = PyInt_FromLong(msg.msg_flags)))
+		/* By getting here, we are sure that all fds have been properly put
+		   into a list if there were any. We don't have to handle cmsgs
+		   anymore. */
+		goto error;
+		PyTuple_SET_ITEM(rv, 2, rvitem);
+
+	/* Make sure rvitem is zeroed. */
+	rvitem = NULL;
+
+	goto finish;
+
+cmsg_error:
+	while(cmsgh) {
+		/* Close all descriptors coming from SCM_RIGHTS, so they don't leak. */
+		if(cmsgh->cmsg_level == SOL_SOCKET &&
+		   cmsgh->cmsg_type == SCM_RIGHTS) {
+			/* Close all additional descriptors by hand, the rest is handled
+			   by destroying the Python objects referencing them. */
+			cmsgdatalen = cmsgh->cmsg_len - sizeof(struct cmsghdr);
+			for(; i < cmsgdatalen; i += sizeof(int))
+				/* Ignore all errors here, these should close... */
+				close(*((int*)(CMSG_DATA(cmsgh) + i)));
+
+			/* Reset i, which isn't reset by loop. */
+			i = 0;
+		}
+
+		/* Go to next message header. */
+		cmsgh = CMSG_NXTHDR(&msg, cmsgh);
+	}
+
+error:
+	Py_XDECREF(newitem);
+	Py_XDECREF(curitem);
+	Py_XDECREF(rvitem);
+	Py_XDECREF(rv);
+	rv = NULL;
+
+finish:
+	if(iov.iov_base) PyMem_Free(iov.iov_base);
+	if(msg.msg_control) PyMem_Free(msg.msg_control);
+	return rv;
+}
+
+PyDoc_STRVAR(recvmsg_doc,
+"recvmsg(len,[addr,[ancmsg,[flags]]]) ->\n\
+(data,[data,...],msg_flags)\n\
+\n\
+Receive data (and possible ancilliary messages) from the remote end\n\
+of this socket. Ancilliary messages are split into tuples, which are\n\
+then returned as a list. In case no message is available, returns an\n\
+empty list. Otherwise the function is similar to recv().");
+
+#define SENDMSG_MAX_ABUFFER_LEN 10240
+
+static PyObject *
+sock_sendmsg(PySocketSockObject *s, PyObject *args)
+{
+	int res = -1, cmsgdatalen, reallen = 0, flags = 0, timeout, *curfd;
+	struct iovec iov;
+	struct msghdr msg;
+	void *cmsgdata;
+	struct cmsghdr *cmsgh;
+	PyObject *rv = NULL, *addrarg = NULL, *iterarg = NULL, *iter = NULL,
+		 *curitem = NULL, *subiter = NULL, *subcuritem = NULL,
+		 *fileno = NULL;
+
+	memset((void*)&iov,0,sizeof(iov));
+	memset((void*)&msg,0,sizeof(msg));
+
+	if(!PyArg_ParseTuple(args,"s#|OOi:sendmsg",
+	   &iov.iov_base,&iov.iov_len,
+	   &addrarg,&iterarg,&flags))
+		goto error;
+
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+
+	if(addrarg && addrarg != Py_None)
+		if(!getsockaddrarg(s, addrarg,
+		   (struct sockaddr**)&msg.msg_name, (int*)&msg.msg_namelen))
+			goto error;
+
+	if(iterarg && iterarg != Py_None) {
+		if(!(iter = PyObject_GetIter(iterarg)))
+			goto error;
+
+		msg.msg_control = PyMem_Malloc(SENDMSG_MAX_ABUFFER_LEN);
+		if(!msg.msg_control) {
+			PyErr_SetString(PyExc_MemoryError,
+				    "no memory left for ancillary buffer.");
+			goto error;
+		}
+		msg.msg_controllen = SENDMSG_MAX_ABUFFER_LEN;
+
+		cmsgh = CMSG_FIRSTHDR(&msg);
+		while((curitem = PyIter_Next(iter))) {
+			if(PyObject_IsInstance(curitem, (PyObject*)&ucred_type)) {
+				/* Calculate correct data length. */
+				cmsgdatalen = sizeof(int)*3;
+
+				/* We got a ucred() structure, parse and send. */
+				if(reallen + CMSG_SPACE(cmsgdatalen) >
+				   SENDMSG_MAX_ABUFFER_LEN) {
+					PyErr_SetString(PyExc_ValueError,
+						"too much ancmsg to send.");
+					goto error;
+				}
+
+				/* Set cmsg level and type. */
+				cmsgh->cmsg_level = SOL_SOCKET;
+				cmsgh->cmsg_type = SCM_CREDENTIALS;
+
+				/* Store data into cmsgdata. */
+				((int*)CMSG_DATA(cmsgh))[1] =
+					((PySocketUcredObject*)curitem)->uid;
+				((int*)CMSG_DATA(cmsgh))[2] =
+					((PySocketUcredObject*)curitem)->gid;
+				((int*)CMSG_DATA(cmsgh))[0] =
+					((PySocketUcredObject*)curitem)->pid;
+			} else if(PyArg_ParseTuple(curitem, "iiz#",
+				  &cmsgh->cmsg_level,
+				  &cmsgh->cmsg_type,
+				  &cmsgdata,
+				  &cmsgdatalen)) {
+				/* We got a string message that isn't parsed any further. */
+				/* Check whether the string data is too long. */
+				if(reallen + CMSG_SPACE(cmsgdatalen) >
+				   SENDMSG_MAX_ABUFFER_LEN) {
+					PyErr_SetString(PyExc_ValueError,
+						"too much ancmsg to send.");
+					goto error;
+				}
+
+				/* If we have data, store it into cmsgdata. */
+				if(cmsgdatalen)
+					memcpy(CMSG_DATA(cmsgh), cmsgdata, cmsgdatalen);
+
+			} else {
+				/* First clear error. */
+				PyErr_Clear();
+
+				/* We probably have a list of file(-like) objects with fileno
+				   methods. */
+				if(!(subiter = PyObject_GetIter(curitem)))
+					goto error;
+
+				/* Initialize pointers. */
+				cmsgdatalen = 0;
+				curfd = (int*)CMSG_DATA(cmsgh);
+
+				/* Set cmsg level and type. */
+				cmsgh->cmsg_level = SOL_SOCKET;
+				cmsgh->cmsg_type = SCM_RIGHTS;
+
+				/* Walk over subitems. */
+				while((subcuritem = PyIter_Next(subiter))) {
+					/* Get fileno of subcuritem. */
+					if(!(fileno = PyObject_CallMethod(subcuritem,
+					   "fileno", NULL)))
+						goto error;
+
+					/* Check for size. */
+					cmsgdatalen += sizeof(int);
+					if(reallen + CMSG_SPACE(cmsgdatalen) >
+					    SENDMSG_MAX_ABUFFER_LEN) {
+						PyErr_SetString(PyExc_ValueError,
+						    "too much ancmsg to send.");
+						goto error;
+					}
+
+					/* Store fileno and check for correct conversion. */
+					*curfd = PyInt_AsLong(fileno);
+					curfd++;
+					if(PyErr_Occurred())
+						goto error;
+
+					/* Remove subcuritem. */
+					Py_DECREF(fileno);
+					Py_DECREF(subcuritem);
+					subcuritem = NULL;
+				}
+
+				/* Remove reference to iterator. */
+				Py_DECREF(subiter);
+				subiter = NULL;
+			}
+
+			/* Set message length and update sent length. */
+			cmsgh->cmsg_len = CMSG_LEN(cmsgdatalen);
+			reallen += CMSG_SPACE(cmsgdatalen);
+
+			/* Remove curitem. */
+			Py_DECREF(curitem);
+			curitem = NULL;
+
+			/* Go to next message in buffer. */
+			cmsgh = CMSG_NXTHDR(&msg, cmsgh);
+		} // while
+
+		msg.msg_controllen = reallen;
+	} // if
+
+	Py_BEGIN_ALLOW_THREADS;
+	timeout = internal_select(s, 1);
+	if (!timeout)
+		res = sendmsg(s->sock_fd, &msg, flags);
+	Py_END_ALLOW_THREADS;
+
+	if (timeout) {
+		PyErr_SetString(socket_timeout, "timed out");
+		goto error;
+	} else if( res == -1 )
+		goto sockerror;
+
+	rv = PyInt_FromLong(res);
+	goto finish;
+
+sockerror:
+	s->errorhandler();
+
+error:
+	Py_XDECREF(rv);
+	rv = NULL;
+
+finish:
+	Py_XDECREF(subcuritem);
+	Py_XDECREF(subiter);
+	Py_XDECREF(curitem);
+	Py_XDECREF(iter);
+	if(msg.msg_control) PyMem_Free(msg.msg_control);
+	return rv;
+}
+
+PyDoc_STRVAR(sendmsg_doc,
+"sendmsg(data,[addr,[[(msg-level,msg-type,msg-data),...],[flags]]]) ->\n\
+sendlen\n\
+\n\
+Send data over a socket. You can additionally send ancilliary messages\n\
+over the socket by specifying them as a list (or any other iterable).\n\
+All other parameters are just as they are to sendto().");
+
 /* List of methods for socket objects */
 
 static PyMethodDef sock_methods[] = {
@@ -2932,6 +3415,10 @@
 			  setsockopt_doc},
 	{"shutdown",	  (PyCFunction)sock_shutdown, METH_O,
 			  shutdown_doc},
+	{"recvmsg",	  (PyCFunction)sock_recvmsg, METH_VARARGS,
+			  recvmsg_doc},
+	{"sendmsg",	  (PyCFunction)sock_sendmsg, METH_VARARGS,
+			  sendmsg_doc},
 #ifdef RISCOS
 	{"sleeptaskw",	  (PyCFunction)sock_sleeptaskw, METH_O,
 	 		  sleeptaskw_doc},
@@ -3082,7 +3569,80 @@
 	PyObject_Del,				/* tp_free */
 };
 
+/* Python interface to struct ucred. */
+#ifdef SO_PASSCRED
 
+static int
+ucred_init(PySocketUcredObject *self, PyObject *args, PyObject *kwargs)
+{
+    static char *kwlist[] = {"uid","gid","pid",NULL};
+
+    if( !PyArg_ParseTupleAndKeywords(args,kwargs,"iii",kwlist,
+				     &self->uid,&self->gid,&self->pid) )
+        return -1;
+
+    return 0;
+}
+
+static PyMemberDef ucred_members[] = {
+    {"uid",T_INT,offsetof(PySocketUcredObject,uid),READONLY,
+     "User ID of communicating process"},
+    {"gid",T_INT,offsetof(PySocketUcredObject,gid),READONLY,
+     "Group ID of communicating process"},
+    {"pid",T_INT,offsetof(PySocketUcredObject,pid),READONLY,
+     "Process ID of communicating process"},
+    {NULL}  /* Sentinel */
+};
+
+static PyMethodDef ucred_methods[] = {
+    {NULL}  /* Sentinel */
+};
+
+static PyTypeObject ucred_type = {
+	PyObject_HEAD_INIT(0)	/* Must fill in type value later */
+	0,					/* ob_size */
+	"_socket.ucred",			/* tp_name */
+	sizeof(PySocketUcredObject),		/* tp_basicsize */
+	0,					/* tp_itemsize */
+	0,		                        /* tp_dealloc */
+	0,					/* tp_print */
+	0,					/* tp_getattr */
+	0,					/* tp_setattr */
+	0,					/* tp_compare */
+	0,			                /* tp_repr */
+	0,					/* tp_as_number */
+	0,					/* tp_as_sequence */
+	0,					/* tp_as_mapping */
+	0,					/* tp_hash */
+	0,					/* tp_call */
+	0,					/* tp_str */
+	0,                                      /* tp_getattro */
+	0,					/* tp_setattro */
+	0,					/* tp_as_buffer */
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+	0,				        /* tp_doc */
+	0,					/* tp_traverse */
+	0,					/* tp_clear */
+	0,					/* tp_richcompare */
+	0,					/* tp_weaklistoffset */
+	0,					/* tp_iter */
+	0,					/* tp_iternext */
+	ucred_methods,	       		        /* tp_methods */
+	ucred_members,	       			/* tp_members */
+	0,					/* tp_getset */
+	0,					/* tp_base */
+	0,					/* tp_dict */
+	0,					/* tp_descr_get */
+	0,					/* tp_descr_set */
+	0,					/* tp_dictoffset */
+	(initproc)ucred_init,    		/* tp_init */
+	0,			                /* tp_alloc */
+	PyType_GenericNew,	       		/* tp_new */
+	0,				        /* tp_free */
+};
+
+#endif
+
 /* Python interface to gethostname(). */
 
 /*ARGSUSED*/
@@ -4435,6 +4995,12 @@
 			       (PyObject *)&sock_type) != 0)
 		return;
 
+	if( PyType_Ready(&ucred_type) < 0 )
+	    return;
+	Py_INCREF(&ucred_type);
+	if( PyModule_AddObject(m,"ucred",(PyObject*)&ucred_type) )
+	    return;
+
 #ifdef ENABLE_IPV6
 	has_ipv6 = Py_True;
 #else
@@ -4728,6 +5294,12 @@
 #ifdef	SO_TYPE
 	PyModule_AddIntConstant(m, "SO_TYPE", SO_TYPE);
 #endif
+#ifdef  SO_PASSCRED
+	PyModule_AddIntConstant(m, "SO_PASSCRED", SO_PASSCRED);
+#endif
+#ifdef  SO_PEERCRED
+	PyModule_AddIntConstant(m, "SO_PEERCRED", SO_PEERCRED);
+#endif
 
 	/* Maximum number of connections for "listen" */
 #ifdef	SOMAXCONN
@@ -4736,6 +5308,14 @@
 	PyModule_AddIntConstant(m, "SOMAXCONN", 5); /* Common value */
 #endif
 
+	/* Ancilliary message types */
+#ifdef  SCM_RIGHTS
+	PyModule_AddIntConstant(m, "SCM_RIGHTS", SCM_RIGHTS);
+#endif
+#ifdef  SCM_CREDENTIALS
+	PyModule_AddIntConstant(m, "SCM_CREDENTIALS", SCM_CREDENTIALS);
+#endif
+
 	/* Flags for send, recv */
 #ifdef	MSG_OOB
 	PyModule_AddIntConstant(m, "MSG_OOB", MSG_OOB);
Index: Modules/socketmodule.h
===================================================================
--- Modules/socketmodule.h	(revision 73434)
+++ Modules/socketmodule.h	(working copy)
@@ -133,6 +133,15 @@
 					    0.0 means non-blocking */
 } PySocketSockObject;
 
+static PyTypeObject ucred_type;
+
+typedef struct {
+    PyObject_HEAD
+    int uid; /* UID of the communicating process. */
+    int gid; /* GID of the communicating process. */
+    int pid; /* PID of the communicating process. */
+} PySocketUcredObject;
+
 /* --- C API ----------------------------------------------------*/
 
 /* Short explanation of what this C API export mechanism does
#
#Help on built-in function sendmsg:
#
#sendmsg(...)
#    sendmsg(data,[addr,[[(msg-level,msg-type,msg-data),...],[flags]]]) ->
#    sendlen
#
#    Send data over a socket. You can additionally send ancilliary messages
#    over the socket by specifying them as a list (or any other iterable).
#    All other parameters are just as they are to sendto().
#
#
#Help on built-in function recvmsg:
#
#recvmsg(...)
#    recvmsg(len,[addr,[ancmsg,[flags]]]) ->
#    (data,[data,...],msg_flags)
#
#    Receive data (and possible ancilliary messages) from the remote end
#    of this socket. Ancilliary messages are split into tuples, which are
#    then returned as a list. In case no message is available, returns an
#    empty list. Otherwise the function is similar to recv().



import socket
import os
import struct

sp = socket.socketpair(socket.AF_UNIX, socket.SOCK_SEQPACKET)

pid = os.fork()
if pid == 0:
	ipc = sp[0]
	sp[1].close()
	print "[+] Child forked"
	fp = ipc.recvmsg(1)[1][0][0]
	print "[+] Child received the file descriptor, the contents:"
	print(fp.read(10000))
else:
	ipc = sp[1]
	sp[0].close()

	fp = open("/etc/passwd")

	print "[+] Parent forked child with pid", pid
	ret = ipc.sendmsg("x", None, [(socket.SOL_SOCKET, socket.SCM_RIGHTS,
			  struct.pack("i", fp.fileno()))], 0)
	print "[+] Parent has sent the open file descriptor"

_______________________________________________
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com

Reply via email to