|
37 | 37 | #include <sys/stat.h>
|
38 | 38 | #include <sys/types.h>
|
39 | 39 | #include <sys/socket.h>
|
| 40 | +#include <sys/un.h> |
40 | 41 | #include <netinet/in.h>
|
41 | 42 | #include <arpa/inet.h>
|
42 | 43 | #include <netdb.h>
|
@@ -82,6 +83,25 @@ static inline mp_obj_t mp_obj_from_sockaddr(const struct sockaddr *addr, socklen
|
82 | 83 | return mp_obj_new_bytes((const byte *)addr, len);
|
83 | 84 | }
|
84 | 85 |
|
| 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 | + |
85 | 105 | static mp_obj_socket_t *socket_new(int fd) {
|
86 | 106 | mp_obj_socket_t *o = mp_obj_malloc(mp_obj_socket_t, &mp_type_socket);
|
87 | 107 | o->fd = fd;
|
@@ -194,8 +214,49 @@ static MP_DEFINE_CONST_FUN_OBJ_1(socket_fileno_obj, socket_fileno);
|
194 | 214 |
|
195 | 215 | static mp_obj_t socket_connect(mp_obj_t self_in, mp_obj_t addr_in) {
|
196 | 216 | mp_obj_socket_t *self = MP_OBJ_TO_PTR(self_in);
|
| 217 | + int family = mp_socket_family_from_fd(self_in); |
197 | 218 | 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); |
199 | 260 |
|
200 | 261 | // special case of PEP 475 to retry only if blocking so we can't use
|
201 | 262 | // MP_HAL_RETRY_SYSCALL() here
|
|
0 commit comments