diff --git a/Doc/lib/libsocket.tex b/Doc/lib/libsocket.tex index 9ff5f0118f0..f78ad2fc210 100644 --- a/Doc/lib/libsocket.tex +++ b/Doc/lib/libsocket.tex @@ -272,10 +272,16 @@ modes, the correct protocol is chosen automatically if the protocol is omitted or zero. \end{funcdesc} -\begin{funcdesc}{getservbyname}{servicename, protocolname} +\begin{funcdesc}{getservbyname}{servicename\optional{, protocolname}} Translate an Internet service name and protocol name to a port number -for that service. The protocol name should be \code{'tcp'} or -\code{'udp'}. +for that service. The optional protocol name, if given, should be +\code{'tcp'} or \code{'udp'}, otherwise any protocol will match. +\end{funcdesc} + +\begin{funcdesc}{getservbyport}{port\optional{, protocolname}} +Translate an Internet port number and protocol name to a service name +for that service. The optional protocol name, if given, should be +\code{'tcp'} or \code{'udp'}, otherwise any protocol will match. \end{funcdesc} \begin{funcdesc}{socket}{\optional{family\optional{, diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 6e2f80c407b..28314548951 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -284,22 +284,34 @@ class GeneralModuleTests(unittest.TestCase): self.assertEqual(swapped & mask, mask) self.assertRaises(OverflowError, func, 1L<<34) - def testGetServByName(self): - # Testing getservbyname() - # try a few protocols - not everyone has telnet enabled - for proto in ("telnet", "ssh", "www", "ftp"): + def testGetServBy(self): + eq = self.assertEqual + # Find one service that exists, then check all the related interfaces. + # I've ordered this by protocols that have both a tcp and udp + # protocol, at least for modern Linuxes. + for service in ('ssh', 'www', 'echo', 'imap2'): try: - socket.getservbyname(proto, 'tcp') - break - except socket.error: - pass - try: - socket.getservbyname(proto, 'udp') + port = socket.getservbyname(service, 'tcp') break except socket.error: pass else: raise socket.error + # Try same call with optional protocol omitted + port2 = socket.getservbyname(service) + eq(port, port2) + # Try udp, but don't barf it it doesn't exist + try: + udpport = socket.getservbyname(service, 'udp') + except socket.error: + udpport = None + else: + eq(udpport, port) + # Now make sure the lookup by port returns the same service name + eq(socket.getservbyport(port2), service) + eq(socket.getservbyport(port, 'tcp'), service) + if udpport is not None: + eq(socket.getservbyport(udpport, 'udp'), service) def testDefaultTimeout(self): # Testing default timeout diff --git a/Misc/NEWS b/Misc/NEWS index 41a79c87511..138afe27932 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -231,6 +231,9 @@ Core and builtins Extension modules ----------------- +- Added socket.getservbyport(), and make the second argument in + getservbyname() and getservbyport() optional. + - time module code that deals with input POSIX timestamps will now raise ValueError if more than a second is lost in precision when the timestamp is cast to the platform C time_t type. There's no chance diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 255e4dfc4d5..46015f16cd4 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -25,7 +25,8 @@ Module interface: - socket.gethostbyaddr(IP address) --> (hostname, [alias, ...], [IP addr, ...]) - socket.gethostname() --> host name (string: 'spam' or 'spam.domain.com') - socket.getprotobyname(protocolname) --> protocol number -- socket.getservbyname(servicename, protocolname) --> port number +- socket.getservbyname(servicename[, protocolname]) --> port number +- socket.getservbyport(portnumber[, protocolname]) --> service name - socket.socket([family[, type [, proto]]]) --> new socket object - socket.ntohs(16 bit value) --> new int object - socket.ntohl(32 bit value) --> new int object @@ -2884,9 +2885,9 @@ for a host. The host argument is a string giving a host name or IP number."); static PyObject * socket_getservbyname(PyObject *self, PyObject *args) { - char *name, *proto; + char *name, *proto=NULL; struct servent *sp; - if (!PyArg_ParseTuple(args, "ss:getservbyname", &name, &proto)) + if (!PyArg_ParseTuple(args, "s|s:getservbyname", &name, &proto)) return NULL; Py_BEGIN_ALLOW_THREADS sp = getservbyname(name, proto); @@ -2899,12 +2900,43 @@ socket_getservbyname(PyObject *self, PyObject *args) } PyDoc_STRVAR(getservbyname_doc, -"getservbyname(servicename, protocolname) -> integer\n\ +"getservbyname(servicename[, protocolname]) -> integer\n\ \n\ Return a port number from a service name and protocol name.\n\ -The protocol name should be 'tcp' or 'udp'."); +The optional protocol name, if given, should be 'tcp' or 'udp',\n\ +otherwise any protocol will match."); +/* Python interface to getservbyport(port). + This only returns the service name, since the other info is already + known or not useful (like the list of aliases). */ + +/*ARGSUSED*/ +static PyObject * +socket_getservbyport(PyObject *self, PyObject *args) +{ + int port; + char *proto=NULL; + struct servent *sp; + if (!PyArg_ParseTuple(args, "i|s:getservbyport", &port, &proto)) + return NULL; + Py_BEGIN_ALLOW_THREADS + sp = getservbyport(htons(port), proto); + Py_END_ALLOW_THREADS + if (sp == NULL) { + PyErr_SetString(socket_error, "port/proto not found"); + return NULL; + } + return PyString_FromString(sp->s_name); +} + +PyDoc_STRVAR(getservbyport_doc, +"getservbyport(port[, protocolname]) -> string\n\ +\n\ +Return the service name from a port number and protocol name.\n\ +The optional protocol name, if given, should be 'tcp' or 'udp',\n\ +otherwise any protocol will match."); + /* Python interface to getprotobyname(name). This only returns the protocol number, since the other info is already known or not useful (like the list of aliases). */ @@ -3530,6 +3562,8 @@ static PyMethodDef socket_methods[] = { METH_VARARGS, gethostname_doc}, {"getservbyname", socket_getservbyname, METH_VARARGS, getservbyname_doc}, + {"getservbyport", socket_getservbyport, + METH_VARARGS, getservbyport_doc}, {"getprotobyname", socket_getprotobyname, METH_VARARGS,getprotobyname_doc}, #ifndef NO_DUP