summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>1991-06-27 15:51:29 (GMT)
committerGuido van Rossum <guido@python.org>1991-06-27 15:51:29 (GMT)
commit30a685f0fe60c11c18a5d7d2bd9abc6981c16276 (patch)
tree1c4bcf7c58d6c997814c4d3b0653f34c93fe5727
parent6574b3ee252f27441c6708b8cbf480fbf42f5431 (diff)
downloadcpython-30a685f0fe60c11c18a5d7d2bd9abc6981c16276.zip
cpython-30a685f0fe60c11c18a5d7d2bd9abc6981c16276.tar.gz
cpython-30a685f0fe60c11c18a5d7d2bd9abc6981c16276.tar.bz2
Many changes to the interface, and added comments.
-rw-r--r--Modules/socketmodule.c404
1 files changed, 326 insertions, 78 deletions
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
index a54a2cb..c0c9079 100644
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -29,30 +29,18 @@ This module provides an interface to Berkeley socket IPC.
Limitations:
-- only AF_INET and AF_UNIX address families
+- only AF_INET and AF_UNIX address families are supported
- no asynchronous I/O
-- no flags on send/receive operations
-- no socket options
-- no protocol parameter on socket() system call
-- although socket objects have read() and write() methods, they can't be
- used everywhere where file objects can be used (e.g., sys.stdout won't
- work and there is no readline() method)
+- no read/write operations (use send/recv instead)
+- no flags on send/recv operations
+- no setsockopt() call
Interface:
-- socket.socket(family, type) returns a new socket object
+- socket.gethostbyname(hostname) --> host IP address (string: 'dd.dd.dd.dd')
+- socket.getservbyname(server, type) --> port number
+- socket.socket(family, type) --> new socket object
- family and type constants from <socket.h> are accessed as socket.AF_INET etc.
-- socket methods are:
- - s.bind(sockaddr) --> None
- - s.connect(sockaddr) --> None
- - s.accept() --> newsocket, sockaddr
- - s.listen(n) --> None
- - s.read(nbytes) --> string
- - s.write(string) --> nbytes
- - s.recvfrom(nbytes) --> string, sockaddr
- - s.sendto(string, sockaddr) --> nbytes
- - s.shutdown(how) --> None
- - s.close() --> None
- errors are reported as the exception socket.error
- an Internet socket address is a pair (hostname, port)
where hostname can be anything recognized by gethostbyname()
@@ -60,9 +48,20 @@ Interface:
- where a hostname is returned, the dd.dd.dd.dd notation is used
- a UNIX domain socket is a string specifying the pathname
-Bugs:
+Socket methods:
+
+- s.bind(sockaddr) --> None
+- s.connect(sockaddr) --> None
+- s.accept() --> new socket object, sockaddr
+- s.listen(n) --> None
+- s.makefile(mode) --> file object
+- s.recv(nbytes) --> string
+- s.recvfrom(nbytes) --> string, sockaddr
+- s.send(string) --> None
+- s.sendto(string, sockaddr) --> None
+- s.shutdown(how) --> None
+- s.close() --> None
-- On the vax, the port numbers seem to be mixed up (when receiving only???)
*/
#include "allobjects.h"
@@ -74,7 +73,15 @@ Bugs:
#include <sys/un.h>
#include <netdb.h>
-static object *SocketError; /* Exception socket.error */
+
+/* Global variable holding the exception type for errors detected
+ by this module (but not argument type or memory errors, etc.). */
+
+static object *SocketError;
+
+
+/* Convenience function to raise an error according to errno
+ and return a NULL pointer from a function. */
static object *
socket_error()
@@ -82,16 +89,35 @@ socket_error()
return err_errno(SocketError);
}
+
+/* The object holding a socket. It holds some extra information,
+ like the address family, which is used to decode socket address
+ arguments properly. */
+
typedef struct {
OB_HEAD
- int sock_fd;
- int sock_family;
- int sock_type;
- int sock_proto;
+ int sock_fd; /* Socket file descriptor */
+ int sock_family; /* Address family, e.g., AF_INET */
+ int sock_type; /* Socket type, e.g., SOCK_STREAM */
+ int sock_proto; /* Protocol type, usually 0 */
} sockobject;
+
+/* A forward reference to the Socktype type object.
+ The Socktype variable contains pointers to various functions,
+ some of which call newsocobject(), which uses Socktype, so
+ there has to be a circular reference. If your compiler complains
+ that it is first declared 'extern' and later 'static', remove the
+ 'static' keyword from the actual definition. */
+
extern typeobject Socktype; /* Forward */
+
+/* Create a new socket object.
+ This just creates the object and initializes it.
+ If the creation fails, return NULL and set an exception (implicit
+ in NEWOBJ()). */
+
static sockobject *
newsockobject(fd, family, type, proto)
int fd, family, type, proto;
@@ -107,22 +133,38 @@ newsockobject(fd, family, type, proto)
return s;
}
-static int setinetaddr PROTO((char *, struct sockaddr_in *));
+
+/* Convert a string specifying a host name or one of a few symbolic
+ names to a numeric IP address. This usually calls gethostbyname()
+ to do the work; the names "" and "<broadcast>" are special.
+ Return the length (should always be 4 bytes), or negative if
+ an error occurred; then an exception is raised. */
+
static int
-setinetaddr(name, addr_ret)
+setipaddr(name, addr_ret)
char *name;
struct sockaddr_in *addr_ret;
{
struct hostent *hp;
+ int d1, d2, d3, d4;
+ char ch;
- if (strcmp(name, "<any>") == 0) {
+ if (name[0] == '\0') {
addr_ret->sin_addr.s_addr = INADDR_ANY;
return 4;
}
- if (strcmp(name, "<broadcast>") == 0) {
+ if (name[0] == '<' && strcmp(name, "<broadcast>") == 0) {
addr_ret->sin_addr.s_addr = INADDR_BROADCAST;
return 4;
}
+ if (sscanf(name, "%d.%d.%d.%d%c", &d1, &d2, &d3, &d4, &ch) == 4 &&
+ 0 <= d1 && d1 <= 255 && 0 <= d2 && d2 <= 255 &&
+ 0 <= d3 && d3 <= 255 && 0 <= d4 && d4 <= 255) {
+ addr_ret->sin_addr.s_addr = htonl(
+ ((long) d1 << 24) | ((long) d2 << 16) |
+ ((long) d3 << 8) | ((long) d4 << 0));
+ return 4;
+ }
hp = gethostbyname(name);
if (hp == NULL) {
err_setstr(SocketError, "host not found");
@@ -132,6 +174,13 @@ setinetaddr(name, addr_ret)
return hp->h_length;
}
+
+/* Generally useful convenience function to create a tuple from two
+ objects. This eats references to the objects; if either is NULL
+ it destroys the other and returns NULL without raising an exception
+ (assuming the function that was called to create the argument must
+ have raised an exception and returned NULL). */
+
static object *
makepair(a, b)
object *a, *b;
@@ -147,32 +196,64 @@ makepair(a, b)
return pair;
}
+
+/* Create a string object representing an IP address.
+ This is always a string of the form 'dd.dd.dd.dd' (with variable
+ size numbers). */
+
+static object *
+makeipaddr(addr)
+ struct sockaddr_in *addr;
+{
+ long x = ntohl(addr->sin_addr.s_addr);
+ char buf[100];
+ sprintf(buf, "%d.%d.%d.%d",
+ (int) (x>>24) & 0xff, (int) (x>>16) & 0xff,
+ (int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff);
+ return newstringobject(buf);
+}
+
+
+/* Create an object representing the given socket address,
+ suitable for passing it back to bind(), connect() etc.
+ The family field of the sockaddr structure is inspected
+ to determine what kind of address it really is. */
+
/*ARGSUSED*/
static object *
makesockaddr(addr, addrlen)
struct sockaddr *addr;
int addrlen;
{
- if (addr->sa_family == AF_INET) {
+ switch (addr->sa_family) {
+
+ case AF_INET:
+ {
struct sockaddr_in *a = (struct sockaddr_in *) addr;
- long x = ntohl(a->sin_addr.s_addr);
- char buf[100];
- sprintf(buf, "%d.%d.%d.%d",
- (int) (x>>24) & 0xff, (int) (x>>16) & 0xff,
- (int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff);
- return makepair(newstringobject(buf),
+ return makepair(makeipaddr(a),
newintobject((long) ntohs(a->sin_port)));
}
- if (addr->sa_family == AF_UNIX) {
+
+ case AF_UNIX:
+ {
struct sockaddr_un *a = (struct sockaddr_un *) addr;
return newstringobject(a->sun_path);
}
- err_setstr(SocketError, "returning unknown socket address type");
- return NULL;
+
+ /* More cases here... */
+
+ default:
+ err_setstr(SocketError, "return unknown socket address type");
+ return NULL;
+ }
}
-static int getsockaddrarg PROTO((sockobject *, object *,
- struct sockaddr **, int *));
+
+/* Parse a socket address argument according to the socket object's
+ address family. Return 1 if the address was in the proper format,
+ 0 of not. The address is returned through addr_ret, its length
+ through len_ret. */
+
static int
getsockaddrarg(s, args, addr_ret, len_ret)
sockobject *s;
@@ -180,13 +261,19 @@ getsockaddrarg(s, args, addr_ret, len_ret)
struct sockaddr **addr_ret;
int *len_ret;
{
- if (s->sock_family == AF_UNIX) {
+ switch (s->sock_family) {
+
+ case AF_UNIX:
+ {
static struct sockaddr_un addr;
object *path;
int len;
- if (!getstrarg(args, &path) ||
- (len = getstringsize(path)) > sizeof addr.sun_path)
+ if (!getstrarg(args, &path))
return 0;
+ if ((len = getstringsize(path)) > sizeof addr.sun_path) {
+ err_setstr(SocketError, "AF_UNIX path too long");
+ return 0;
+ }
addr.sun_family = AF_UNIX;
memcpy(addr.sun_path, getstringvalue(path), len);
*addr_ret = (struct sockaddr *) &addr;
@@ -194,13 +281,14 @@ getsockaddrarg(s, args, addr_ret, len_ret)
return 1;
}
- if (s->sock_family == AF_INET) {
+ case AF_INET:
+ {
static struct sockaddr_in addr;
object *host;
int port;
if (!getstrintarg(args, &host, &port))
return 0;
- if (setinetaddr(getstringvalue(host), &addr) < 0)
+ if (setipaddr(getstringvalue(host), &addr) < 0)
return 0;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
@@ -209,10 +297,18 @@ getsockaddrarg(s, args, addr_ret, len_ret)
return 1;
}
- err_setstr(SocketError, "getsockaddrarg: bad family");
- return 0;
+ /* More cases here... */
+
+ default:
+ err_setstr(SocketError, "getsockaddrarg: bad family");
+ return 0;
+
+ }
}
+
+/* s.accept() method */
+
static object *
sock_accept(s, args)
sockobject *s;
@@ -227,6 +323,8 @@ sock_accept(s, args)
newfd = accept(s->sock_fd, (struct sockaddr *) addrbuf, &addrlen);
if (newfd < 0)
return socket_error();
+ /* Create the new object with unspecified family,
+ to avoid calls to bind() etc. on it. */
res = makepair((object *) newsockobject(newfd, AF_UNSPEC, 0, 0),
makesockaddr((struct sockaddr *) addrbuf, addrlen));
if (res == NULL)
@@ -234,6 +332,9 @@ sock_accept(s, args)
return res;
}
+
+/* s.bind(sockaddr) method */
+
static object *
sock_bind(s, args)
sockobject *s;
@@ -249,6 +350,11 @@ sock_bind(s, args)
return None;
}
+
+/* s.close() method.
+ Set the file descriptor to -1 so operations tried subsequently
+ will surely fail. */
+
static object *
sock_close(s, args)
sockobject *s;
@@ -262,6 +368,9 @@ sock_close(s, args)
return None;
}
+
+/* s.connect(sockaddr) method */
+
static object *
sock_connect(s, args)
sockobject *s;
@@ -277,6 +386,9 @@ sock_connect(s, args)
return None;
}
+
+/* s.listen(n) method */
+
static object *
sock_listen(s, args)
sockobject *s;
@@ -291,19 +403,51 @@ sock_listen(s, args)
return None;
}
+
+/* s.makefile(mode) method.
+ Create a new open file object referring to a dupped version of
+ the socket's file descriptor. (The dup() call is necessary so
+ that the open file and socket objects may be closed independent
+ of each other.)
+ The mode argument specifies 'r' or 'w' passed to fdopen(). */
+
static object *
-sock_read(s, args)
+sock_makefile(s, args)
sockobject *s;
object *args;
{
- int len, n;
- object *buf;
- if (!getintarg(args, &len))
+ extern int fclose PROTO((FILE *));
+ object *mode;
+ int fd;
+ FILE *fp;
+ if (!getstrarg(args, &mode))
return NULL;
+ if ((fd = dup(s->sock_fd)) < 0 ||
+ (fp = fdopen(fd, getstringvalue(mode))) == NULL)
+ return socket_error();
+ return newopenfileobject(fp, "<socket>", getstringvalue(mode), fclose);
+}
+
+
+/* s.recv(nbytes) method */
+
+static object *
+sock_recv(s, args)
+ sockobject *s;
+ object *args;
+{
+ int len, n, flags;
+ object *buf;
+ if (!getintintarg(args, &len, &flags)) {
+ err_clear();
+ if (!getintarg(args, &len))
+ return NULL;
+ flags = 0;
+ }
buf = newsizedstringobject((char *) 0, len);
if (buf == NULL)
return NULL;
- n = read(s->sock_fd, getstringvalue(buf), len);
+ n = recv(s->sock_fd, getstringvalue(buf), len, flags);
if (n < 0)
return socket_error();
if (resizestring(&buf, n) < 0)
@@ -311,6 +455,9 @@ sock_read(s, args)
return buf;
}
+
+/* s.recvfrom(nbytes) method */
+
static object *
sock_recvfrom(s, args)
sockobject *s;
@@ -332,6 +479,33 @@ sock_recvfrom(s, args)
return makepair(buf, makesockaddr(addrbuf, addrlen));
}
+
+/* s.send(data) method */
+
+static object *
+sock_send(s, args)
+ sockobject *s;
+ object *args;
+{
+ object *buf;
+ int len, n, flags;
+ if (!getstrintarg(args, &buf, &flags)) {
+ err_clear();
+ if (!getstrarg(args, &buf))
+ return NULL;
+ flags = 0;
+ }
+ len = getstringsize(buf);
+ n = send(s->sock_fd, getstringvalue(buf), len, flags);
+ if (n < 0)
+ return socket_error();
+ INCREF(None);
+ return None;
+}
+
+
+/* s.sendto(data, sockaddr) method */
+
static object *
sock_sendto(s, args)
sockobject *s;
@@ -352,9 +526,13 @@ sock_sendto(s, args)
addr, addrlen);
if (n < 0)
return socket_error();
- return newintobject((long) n);
+ INCREF(None);
+ return None;
}
+
+/* s.shutdown(how) method */
+
static object *
sock_shutdown(s, args)
sockobject *s;
@@ -369,21 +547,8 @@ sock_shutdown(s, args)
return None;
}
-static object *
-sock_write(s, args)
- sockobject *s;
- object *args;
-{
- object *buf;
- int len, n;
- if (!getstrarg(args, &buf))
- return NULL;
- len = getstringsize(buf);
- n = write(s->sock_fd, getstringvalue(buf), len);
- if (n < 0)
- return socket_error();
- return newintobject((long) n);
-}
+
+/* List of methods for socket objects */
static struct methodlist sock_methods[] = {
{"accept", sock_accept},
@@ -391,14 +556,19 @@ static struct methodlist sock_methods[] = {
{"close", sock_close},
{"connect", sock_connect},
{"listen", sock_listen},
- {"read", sock_read},
+ {"makefile", sock_makefile},
+ {"recv", sock_recv},
{"recvfrom", sock_recvfrom},
+ {"send", sock_send},
{"sendto", sock_sendto},
{"shutdown", sock_shutdown},
- {"write", sock_write},
{NULL, NULL} /* sentinel */
};
+
+/* Deallocate a socket object in response to the last DECREF().
+ First close the file description. */
+
static void
sock_dealloc(s)
sockobject *s;
@@ -407,6 +577,9 @@ sock_dealloc(s)
DEL(s);
}
+
+/* Return a socket object's named attribute. */
+
static object *
sock_getattr(s, name)
sockobject *s;
@@ -415,6 +588,11 @@ sock_getattr(s, name)
return findmethod(sock_methods, (object *) s, name);
}
+
+/* Type object for socket objects.
+ If your compiler complains that it is first declared 'extern'
+ and later 'static', remove the 'static' keyword here. */
+
static typeobject Socktype = {
OB_HEAD_INIT(&Typetype)
0,
@@ -432,6 +610,53 @@ static typeobject Socktype = {
0, /*tp_as_mapping*/
};
+
+/* Python interface to gethostbyname(name). */
+
+/*ARGSUSED*/
+static object *
+socket_gethostbyname(self, args)
+ object *self;
+ object *args;
+{
+ object *name;
+ struct hostent *hp;
+ struct sockaddr_in addrbuf;
+ if (!getstrarg(args, &name))
+ return NULL;
+ if (setipaddr(getstringvalue(name), &addrbuf) < 0)
+ return NULL;
+ return makeipaddr(&addrbuf);
+}
+
+
+/* Python interface to getservbyname(name).
+ This only returns the port number, since the other info is already
+ known or not useful (like the list of aliases). */
+
+/*ARGSUSED*/
+static object *
+socket_getservbyname(self, args)
+ object *self;
+ object *args;
+{
+ object *name, *proto;
+ struct servent *sp;
+ if (!getstrstrarg(args, &name, &proto))
+ return NULL;
+ sp = getservbyname(getstringvalue(name), getstringvalue(proto));
+ if (sp == NULL) {
+ err_setstr(SocketError, "service/proto not found");
+ return NULL;
+ }
+ return newintobject((long) ntohs(sp->s_port));
+}
+
+
+/* Python interface to socket(family, type, proto).
+ The third (protocol) argument is optional.
+ Return a new socket object. */
+
/*ARGSUSED*/
static object *
socket_socket(self, args)
@@ -440,23 +665,40 @@ socket_socket(self, args)
{
sockobject *s;
int family, type, proto, fd;
- if (!getintintarg(args, &family, &type))
- return NULL;
- proto = 0;
+ if (args != NULL && is_tupleobject(args) && gettuplesize(args) == 3) {
+ if (!getintintarg(args, &family, &type, &proto))
+ return NULL;
+ }
+ else {
+ if (!getintintarg(args, &family, &type))
+ return NULL;
+ proto = 0;
+ }
fd = socket(family, type, proto);
if (fd < 0)
return socket_error();
s = newsockobject(fd, family, type, proto);
+ /* If the object can't be created, don't forget to close the
+ file descriptor again! */
if (s == NULL)
- close(fd);
+ (void) close(fd);
return (object *) s;
}
+
+/* List of functions exported by this module. */
+
static struct methodlist socket_methods[] = {
- {"socket", socket_socket},
- {NULL, NULL} /* Sentinel */
+ {"gethostbyname", socket_gethostbyname},
+ {"getservbyname", socket_getservbyname},
+ {"socket", socket_socket},
+ {NULL, NULL} /* Sentinel */
};
+
+/* Convenience routine to export an integer value.
+ For simplicity, errors (which are unlikely anyway) are ignored. */
+
static void
insint(d, name, value)
object *d;
@@ -474,6 +716,12 @@ insint(d, name, value)
}
}
+
+/* Initialize this module.
+ This is called when the first 'import socket' is done,
+ via a table in config.c, if config.c is compiled with USE_SOCKET
+ defined. */
+
void
initsocket()
{