Skip to content

Commit dcdd59a

Browse files
committed
unix/modsocket: Accept a host+port array for socket.connect.
This commit lets socket.connect accept an array containing a hostname string and a port number as the address to connect to, if the socket family is either AF_INET or AF_INET6. This brings the behaviour of socket.connect in line with the other embedded ports' versions that use LWIP, and also with CPython - even though CPython can only accept a tuple as its host+port argument. The existing behaviour of accepting a serialised sockaddr structure as the connection address is still present, maintaining compatibility with existing code. Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
1 parent a05766f commit dcdd59a

File tree

1 file changed

+62
-1
lines changed

1 file changed

+62
-1
lines changed

ports/unix/modsocket.c

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include <sys/stat.h>
3838
#include <sys/types.h>
3939
#include <sys/socket.h>
40+
#include <sys/un.h>
4041
#include <netinet/in.h>
4142
#include <arpa/inet.h>
4243
#include <netdb.h>
@@ -82,6 +83,25 @@ static inline mp_obj_t mp_obj_from_sockaddr(const struct sockaddr *addr, socklen
8283
return mp_obj_new_bytes((const byte *)addr, len);
8384
}
8485

86+
static int mp_socket_family_from_fd(mp_obj_t socket_in) {
87+
MP_STATIC_ASSERT(sizeof(struct sockaddr_un) > sizeof(struct sockaddr_in6));
88+
mp_obj_socket_t *socket = MP_OBJ_TO_PTR(socket_in);
89+
// A sockaddr_un struct is big enough to store either a sockaddr_in6 or a
90+
// sockaddr_in.
91+
struct sockaddr_un address;
92+
socklen_t address_len = sizeof(struct sockaddr_un);
93+
MP_THREAD_GIL_EXIT();
94+
int r = getsockname(socket->fd, (struct sockaddr *)&address, &address_len);
95+
MP_THREAD_GIL_ENTER();
96+
// sockaddr_un, sockaddr_in6, and sockaddr_in share the same field
97+
// structure for the first two fields, and the family identifier happens
98+
// to be the first one.
99+
return r == -1 ? -1 : address.sun_family;
100+
}
101+
102+
// Forward definitions
103+
static mp_obj_t mod_socket_getaddrinfo(size_t n_args, const mp_obj_t *args);
104+
85105
static mp_obj_socket_t *socket_new(int fd) {
86106
mp_obj_socket_t *o = mp_obj_malloc(mp_obj_socket_t, &mp_type_socket);
87107
o->fd = fd;
@@ -194,8 +214,49 @@ static MP_DEFINE_CONST_FUN_OBJ_1(socket_fileno_obj, socket_fileno);
194214

195215
static mp_obj_t socket_connect(mp_obj_t self_in, mp_obj_t addr_in) {
196216
mp_obj_socket_t *self = MP_OBJ_TO_PTR(self_in);
217+
int family = mp_socket_family_from_fd(self_in);
197218
mp_buffer_info_t bufinfo;
198-
mp_get_buffer_raise(addr_in, &bufinfo, MP_BUFFER_READ);
219+
mp_obj_t addr_src;
220+
221+
if ((mp_obj_is_type(addr_in, &mp_type_tuple) || mp_obj_is_type(addr_in, &mp_type_list)) && (family == AF_INET || family == AF_INET6)) {
222+
// Check if the address is in the form <"host", port> for a socket
223+
// that is either of type AF_INET or AF_INET6, and if so perform
224+
// name resolution via getaddrinfo. This deviates slightly from
225+
// CPython in two ways:
226+
//
227+
// * Numeric host addresses are not supported, whilst CPython also
228+
// supports numeric addresses (and probably much more).
229+
// * socket.connect argument can be either a tuple or a list,
230+
// whilst CPython only accepts tuples for AF_INET or AF_INET6
231+
// sockets.
232+
//
233+
// Another limitation that is shared with CPython is that if a name
234+
// resolves to multiple addresses for the given family, the first
235+
// one is always the one the socket will attempt to connect to.
236+
//
237+
// For more complex requirements, then the usual method of calling
238+
// socket.getaddrinfo yourself and pass the raw buffer data should
239+
// allow handling of pretty much all possible conditions.
240+
241+
mp_obj_t *addr_args;
242+
size_t addr_len;
243+
mp_obj_get_array(addr_in, &addr_len, &addr_args);
244+
if (addr_len != 2) {
245+
mp_raise_ValueError(MP_ERROR_TEXT("address must contain two elements"));
246+
}
247+
mp_obj_t info_args[3] = { addr_args[0], addr_args[1], MP_OBJ_NEW_SMALL_INT(family) };
248+
mp_obj_t info = mod_socket_getaddrinfo(MP_ARRAY_SIZE(info_args), info_args);
249+
mp_obj_list_t *list = MP_OBJ_TO_PTR(info);
250+
if (list->len == 0) {
251+
mp_raise_OSError(MP_ENOENT);
252+
}
253+
mp_obj_tuple_t *addr_tuple = MP_OBJ_TO_PTR(list->items[0]);
254+
addr_src = addr_tuple->items[4];
255+
} else {
256+
// Default to the usual pre-resolved sockaddr representation.
257+
addr_src = addr_in;
258+
}
259+
mp_get_buffer_raise(addr_src, &bufinfo, MP_BUFFER_READ);
199260

200261
// special case of PEP 475 to retry only if blocking so we can't use
201262
// MP_HAL_RETRY_SYSCALL() here

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy