Many changes to the interface, and added comments.

This commit is contained in:
Guido van Rossum 1991-06-27 15:51:29 +00:00
parent 6574b3ee25
commit 30a685f0fe
1 changed files with 326 additions and 78 deletions

View File

@ -29,30 +29,18 @@ This module provides an interface to Berkeley socket IPC.
Limitations: Limitations:
- only AF_INET and AF_UNIX address families - only AF_INET and AF_UNIX address families are supported
- no asynchronous I/O - no asynchronous I/O
- no flags on send/receive operations - no read/write operations (use send/recv instead)
- no socket options - no flags on send/recv operations
- no protocol parameter on socket() system call - no setsockopt() 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)
Interface: 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. - 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 - errors are reported as the exception socket.error
- an Internet socket address is a pair (hostname, port) - an Internet socket address is a pair (hostname, port)
where hostname can be anything recognized by gethostbyname() 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 - where a hostname is returned, the dd.dd.dd.dd notation is used
- a UNIX domain socket is a string specifying the pathname - 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" #include "allobjects.h"
@ -74,7 +73,15 @@ Bugs:
#include <sys/un.h> #include <sys/un.h>
#include <netdb.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 * static object *
socket_error() socket_error()
@ -82,16 +89,35 @@ socket_error()
return err_errno(SocketError); 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 { typedef struct {
OB_HEAD OB_HEAD
int sock_fd; int sock_fd; /* Socket file descriptor */
int sock_family; int sock_family; /* Address family, e.g., AF_INET */
int sock_type; int sock_type; /* Socket type, e.g., SOCK_STREAM */
int sock_proto; int sock_proto; /* Protocol type, usually 0 */
} sockobject; } 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 */ 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 * static sockobject *
newsockobject(fd, family, type, proto) newsockobject(fd, family, type, proto)
int fd, family, type, proto; int fd, family, type, proto;
@ -107,22 +133,38 @@ newsockobject(fd, family, type, proto)
return s; 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 static int
setinetaddr(name, addr_ret) setipaddr(name, addr_ret)
char *name; char *name;
struct sockaddr_in *addr_ret; struct sockaddr_in *addr_ret;
{ {
struct hostent *hp; 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; addr_ret->sin_addr.s_addr = INADDR_ANY;
return 4; return 4;
} }
if (strcmp(name, "<broadcast>") == 0) { if (name[0] == '<' && strcmp(name, "<broadcast>") == 0) {
addr_ret->sin_addr.s_addr = INADDR_BROADCAST; addr_ret->sin_addr.s_addr = INADDR_BROADCAST;
return 4; 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); hp = gethostbyname(name);
if (hp == NULL) { if (hp == NULL) {
err_setstr(SocketError, "host not found"); err_setstr(SocketError, "host not found");
@ -132,6 +174,13 @@ setinetaddr(name, addr_ret)
return hp->h_length; 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 * static object *
makepair(a, b) makepair(a, b)
object *a, *b; object *a, *b;
@ -147,32 +196,64 @@ makepair(a, b)
return pair; 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*/ /*ARGSUSED*/
static object * static object *
makesockaddr(addr, addrlen) makesockaddr(addr, addrlen)
struct sockaddr *addr; struct sockaddr *addr;
int addrlen; int addrlen;
{ {
if (addr->sa_family == AF_INET) { switch (addr->sa_family) {
case AF_INET:
{
struct sockaddr_in *a = (struct sockaddr_in *) addr; struct sockaddr_in *a = (struct sockaddr_in *) addr;
long x = ntohl(a->sin_addr.s_addr); return makepair(makeipaddr(a),
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),
newintobject((long) ntohs(a->sin_port))); newintobject((long) ntohs(a->sin_port)));
} }
if (addr->sa_family == AF_UNIX) {
case AF_UNIX:
{
struct sockaddr_un *a = (struct sockaddr_un *) addr; struct sockaddr_un *a = (struct sockaddr_un *) addr;
return newstringobject(a->sun_path); 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 static int
getsockaddrarg(s, args, addr_ret, len_ret) getsockaddrarg(s, args, addr_ret, len_ret)
sockobject *s; sockobject *s;
@ -180,13 +261,19 @@ getsockaddrarg(s, args, addr_ret, len_ret)
struct sockaddr **addr_ret; struct sockaddr **addr_ret;
int *len_ret; int *len_ret;
{ {
if (s->sock_family == AF_UNIX) { switch (s->sock_family) {
case AF_UNIX:
{
static struct sockaddr_un addr; static struct sockaddr_un addr;
object *path; object *path;
int len; int len;
if (!getstrarg(args, &path) || if (!getstrarg(args, &path))
(len = getstringsize(path)) > sizeof addr.sun_path)
return 0; 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; addr.sun_family = AF_UNIX;
memcpy(addr.sun_path, getstringvalue(path), len); memcpy(addr.sun_path, getstringvalue(path), len);
*addr_ret = (struct sockaddr *) &addr; *addr_ret = (struct sockaddr *) &addr;
@ -194,13 +281,14 @@ getsockaddrarg(s, args, addr_ret, len_ret)
return 1; return 1;
} }
if (s->sock_family == AF_INET) { case AF_INET:
{
static struct sockaddr_in addr; static struct sockaddr_in addr;
object *host; object *host;
int port; int port;
if (!getstrintarg(args, &host, &port)) if (!getstrintarg(args, &host, &port))
return 0; return 0;
if (setinetaddr(getstringvalue(host), &addr) < 0) if (setipaddr(getstringvalue(host), &addr) < 0)
return 0; return 0;
addr.sin_family = AF_INET; addr.sin_family = AF_INET;
addr.sin_port = htons(port); addr.sin_port = htons(port);
@ -209,10 +297,18 @@ getsockaddrarg(s, args, addr_ret, len_ret)
return 1; return 1;
} }
err_setstr(SocketError, "getsockaddrarg: bad family"); /* More cases here... */
return 0;
default:
err_setstr(SocketError, "getsockaddrarg: bad family");
return 0;
}
} }
/* s.accept() method */
static object * static object *
sock_accept(s, args) sock_accept(s, args)
sockobject *s; sockobject *s;
@ -227,6 +323,8 @@ sock_accept(s, args)
newfd = accept(s->sock_fd, (struct sockaddr *) addrbuf, &addrlen); newfd = accept(s->sock_fd, (struct sockaddr *) addrbuf, &addrlen);
if (newfd < 0) if (newfd < 0)
return socket_error(); 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), res = makepair((object *) newsockobject(newfd, AF_UNSPEC, 0, 0),
makesockaddr((struct sockaddr *) addrbuf, addrlen)); makesockaddr((struct sockaddr *) addrbuf, addrlen));
if (res == NULL) if (res == NULL)
@ -234,6 +332,9 @@ sock_accept(s, args)
return res; return res;
} }
/* s.bind(sockaddr) method */
static object * static object *
sock_bind(s, args) sock_bind(s, args)
sockobject *s; sockobject *s;
@ -249,6 +350,11 @@ sock_bind(s, args)
return None; return None;
} }
/* s.close() method.
Set the file descriptor to -1 so operations tried subsequently
will surely fail. */
static object * static object *
sock_close(s, args) sock_close(s, args)
sockobject *s; sockobject *s;
@ -262,6 +368,9 @@ sock_close(s, args)
return None; return None;
} }
/* s.connect(sockaddr) method */
static object * static object *
sock_connect(s, args) sock_connect(s, args)
sockobject *s; sockobject *s;
@ -277,6 +386,9 @@ sock_connect(s, args)
return None; return None;
} }
/* s.listen(n) method */
static object * static object *
sock_listen(s, args) sock_listen(s, args)
sockobject *s; sockobject *s;
@ -291,19 +403,51 @@ sock_listen(s, args)
return None; 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 * static object *
sock_read(s, args) sock_makefile(s, args)
sockobject *s; sockobject *s;
object *args; object *args;
{ {
int len, n; extern int fclose PROTO((FILE *));
object *buf; object *mode;
if (!getintarg(args, &len)) int fd;
FILE *fp;
if (!getstrarg(args, &mode))
return NULL; 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); buf = newsizedstringobject((char *) 0, len);
if (buf == NULL) if (buf == NULL)
return NULL; return NULL;
n = read(s->sock_fd, getstringvalue(buf), len); n = recv(s->sock_fd, getstringvalue(buf), len, flags);
if (n < 0) if (n < 0)
return socket_error(); return socket_error();
if (resizestring(&buf, n) < 0) if (resizestring(&buf, n) < 0)
@ -311,6 +455,9 @@ sock_read(s, args)
return buf; return buf;
} }
/* s.recvfrom(nbytes) method */
static object * static object *
sock_recvfrom(s, args) sock_recvfrom(s, args)
sockobject *s; sockobject *s;
@ -332,6 +479,33 @@ sock_recvfrom(s, args)
return makepair(buf, makesockaddr(addrbuf, addrlen)); 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 * static object *
sock_sendto(s, args) sock_sendto(s, args)
sockobject *s; sockobject *s;
@ -352,9 +526,13 @@ sock_sendto(s, args)
addr, addrlen); addr, addrlen);
if (n < 0) if (n < 0)
return socket_error(); return socket_error();
return newintobject((long) n); INCREF(None);
return None;
} }
/* s.shutdown(how) method */
static object * static object *
sock_shutdown(s, args) sock_shutdown(s, args)
sockobject *s; sockobject *s;
@ -369,21 +547,8 @@ sock_shutdown(s, args)
return None; return None;
} }
static object *
sock_write(s, args) /* List of methods for socket objects */
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);
}
static struct methodlist sock_methods[] = { static struct methodlist sock_methods[] = {
{"accept", sock_accept}, {"accept", sock_accept},
@ -391,14 +556,19 @@ static struct methodlist sock_methods[] = {
{"close", sock_close}, {"close", sock_close},
{"connect", sock_connect}, {"connect", sock_connect},
{"listen", sock_listen}, {"listen", sock_listen},
{"read", sock_read}, {"makefile", sock_makefile},
{"recv", sock_recv},
{"recvfrom", sock_recvfrom}, {"recvfrom", sock_recvfrom},
{"send", sock_send},
{"sendto", sock_sendto}, {"sendto", sock_sendto},
{"shutdown", sock_shutdown}, {"shutdown", sock_shutdown},
{"write", sock_write},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
/* Deallocate a socket object in response to the last DECREF().
First close the file description. */
static void static void
sock_dealloc(s) sock_dealloc(s)
sockobject *s; sockobject *s;
@ -407,6 +577,9 @@ sock_dealloc(s)
DEL(s); DEL(s);
} }
/* Return a socket object's named attribute. */
static object * static object *
sock_getattr(s, name) sock_getattr(s, name)
sockobject *s; sockobject *s;
@ -415,6 +588,11 @@ sock_getattr(s, name)
return findmethod(sock_methods, (object *) 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 = { static typeobject Socktype = {
OB_HEAD_INIT(&Typetype) OB_HEAD_INIT(&Typetype)
0, 0,
@ -432,6 +610,53 @@ static typeobject Socktype = {
0, /*tp_as_mapping*/ 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*/ /*ARGSUSED*/
static object * static object *
socket_socket(self, args) socket_socket(self, args)
@ -440,23 +665,40 @@ socket_socket(self, args)
{ {
sockobject *s; sockobject *s;
int family, type, proto, fd; int family, type, proto, fd;
if (!getintintarg(args, &family, &type)) if (args != NULL && is_tupleobject(args) && gettuplesize(args) == 3) {
return NULL; if (!getintintarg(args, &family, &type, &proto))
proto = 0; return NULL;
}
else {
if (!getintintarg(args, &family, &type))
return NULL;
proto = 0;
}
fd = socket(family, type, proto); fd = socket(family, type, proto);
if (fd < 0) if (fd < 0)
return socket_error(); return socket_error();
s = newsockobject(fd, family, type, proto); 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) if (s == NULL)
close(fd); (void) close(fd);
return (object *) s; return (object *) s;
} }
/* List of functions exported by this module. */
static struct methodlist socket_methods[] = { static struct methodlist socket_methods[] = {
{"socket", socket_socket}, {"gethostbyname", socket_gethostbyname},
{NULL, NULL} /* Sentinel */ {"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 static void
insint(d, name, value) insint(d, name, value)
object *d; 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 void
initsocket() initsocket()
{ {