Paul Sokolovsky | fc92608 | 2014-01-18 23:47:44 +0200 | [diff] [blame] | 1 | #include <stdio.h> |
| 2 | #include <assert.h> |
| 3 | #include <string.h> |
| 4 | #include <unistd.h> |
| 5 | #include <sys/socket.h> |
| 6 | #include <arpa/inet.h> |
| 7 | #include <netdb.h> |
| 8 | #include <errno.h> |
| 9 | |
| 10 | #include "nlr.h" |
| 11 | #include "misc.h" |
| 12 | #include "mpconfig.h" |
Damien George | 55baff4 | 2014-01-21 21:40:13 +0000 | [diff] [blame^] | 13 | #include "qstr.h" |
Paul Sokolovsky | fc92608 | 2014-01-18 23:47:44 +0200 | [diff] [blame] | 14 | #include "obj.h" |
| 15 | #include "objtuple.h" |
| 16 | #include "objarray.h" |
| 17 | #include "runtime.h" |
| 18 | #include "stream.h" |
| 19 | |
| 20 | #define MICROPY_SOCKET_EXTRA (0) |
| 21 | |
| 22 | typedef struct _mp_obj_socket_t { |
| 23 | mp_obj_base_t base; |
| 24 | int fd; |
| 25 | } mp_obj_socket_t; |
| 26 | |
| 27 | static const mp_obj_type_t rawsocket_type; |
| 28 | |
| 29 | // Helper functions |
| 30 | #define RAISE_ERRNO(err_flag, error_val) \ |
| 31 | { if (err_flag == -1) \ |
| 32 | { nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_OSError, "[Errno %d]", error_val)); } } |
| 33 | |
| 34 | static void get_buffer(mp_obj_t obj, buffer_info_t *bufinfo) { |
| 35 | mp_obj_base_t *o = (mp_obj_base_t *)obj; |
| 36 | if (o->type->buffer_p.get_buffer == NULL) { |
| 37 | goto error; |
| 38 | } |
| 39 | o->type->buffer_p.get_buffer(o, bufinfo, BUFFER_READ); |
| 40 | if (bufinfo->buf == NULL) { |
| 41 | goto error; |
| 42 | } |
| 43 | return; |
| 44 | |
| 45 | error: |
| 46 | nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "Operation not supported")); |
| 47 | } |
| 48 | |
| 49 | static mp_obj_socket_t *socket_new(int fd) { |
| 50 | mp_obj_socket_t *o = m_new_obj(mp_obj_socket_t); |
| 51 | o->base.type = &rawsocket_type; |
| 52 | o->fd = fd; |
| 53 | return o; |
| 54 | } |
| 55 | |
| 56 | |
| 57 | static void socket_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) { |
| 58 | mp_obj_socket_t *self = self_in; |
| 59 | print(env, "<_socket %d>", self->fd); |
| 60 | } |
| 61 | |
| 62 | static machine_int_t socket_read(mp_obj_t o_in, void *buf, machine_uint_t size, int *errcode) { |
| 63 | mp_obj_socket_t *o = o_in; |
| 64 | machine_int_t r = read(o->fd, buf, size); |
| 65 | if (r == -1) { |
| 66 | *errcode = errno; |
| 67 | } |
| 68 | return r; |
| 69 | } |
| 70 | |
| 71 | static machine_int_t socket_write(mp_obj_t o_in, const void *buf, machine_uint_t size, int *errcode) { |
| 72 | mp_obj_socket_t *o = o_in; |
| 73 | machine_int_t r = write(o->fd, buf, size); |
| 74 | if (r == -1) { |
| 75 | *errcode = errno; |
| 76 | } |
| 77 | return r; |
| 78 | } |
| 79 | |
| 80 | static mp_obj_t socket_close(mp_obj_t self_in) { |
| 81 | mp_obj_socket_t *self = self_in; |
| 82 | close(self->fd); |
| 83 | return mp_const_none; |
| 84 | } |
| 85 | static MP_DEFINE_CONST_FUN_OBJ_1(socket_close_obj, socket_close); |
| 86 | |
| 87 | static mp_obj_t socket_connect(mp_obj_t self_in, mp_obj_t addr_in) { |
| 88 | mp_obj_socket_t *self = self_in; |
| 89 | buffer_info_t bufinfo; |
| 90 | get_buffer(addr_in, &bufinfo); |
| 91 | int r = connect(self->fd, (const struct sockaddr *)bufinfo.buf, bufinfo.len); |
| 92 | RAISE_ERRNO(r, errno); |
| 93 | return mp_const_none; |
| 94 | } |
| 95 | static MP_DEFINE_CONST_FUN_OBJ_2(socket_connect_obj, socket_connect); |
| 96 | |
| 97 | static mp_obj_t socket_bind(mp_obj_t self_in, mp_obj_t addr_in) { |
| 98 | mp_obj_socket_t *self = self_in; |
| 99 | buffer_info_t bufinfo; |
| 100 | get_buffer(addr_in, &bufinfo); |
| 101 | int r = bind(self->fd, (const struct sockaddr *)bufinfo.buf, bufinfo.len); |
| 102 | RAISE_ERRNO(r, errno); |
| 103 | return mp_const_none; |
| 104 | } |
| 105 | static MP_DEFINE_CONST_FUN_OBJ_2(socket_bind_obj, socket_bind); |
| 106 | |
| 107 | static mp_obj_t socket_listen(mp_obj_t self_in, mp_obj_t backlog_in) { |
| 108 | mp_obj_socket_t *self = self_in; |
| 109 | int r = listen(self->fd, MP_OBJ_SMALL_INT_VALUE(backlog_in)); |
| 110 | RAISE_ERRNO(r, errno); |
| 111 | return mp_const_none; |
| 112 | } |
| 113 | static MP_DEFINE_CONST_FUN_OBJ_2(socket_listen_obj, socket_listen); |
| 114 | |
| 115 | static mp_obj_t socket_accept(mp_obj_t self_in) { |
| 116 | mp_obj_socket_t *self = self_in; |
| 117 | struct sockaddr addr; |
| 118 | socklen_t addr_len = sizeof(addr); |
| 119 | int fd = accept(self->fd, &addr, &addr_len); |
| 120 | RAISE_ERRNO(fd, errno); |
| 121 | |
| 122 | mp_obj_tuple_t *t = mp_obj_new_tuple(2, NULL); |
| 123 | t->items[0] = socket_new(fd); |
| 124 | t->items[1] = mp_obj_new_bytearray(addr_len, &addr); |
| 125 | |
| 126 | return t; |
| 127 | } |
| 128 | static MP_DEFINE_CONST_FUN_OBJ_1(socket_accept_obj, socket_accept); |
| 129 | |
| 130 | static mp_obj_t socket_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) { |
| 131 | int family = AF_INET; |
| 132 | int type = SOCK_STREAM; |
| 133 | int proto = 0; |
| 134 | |
| 135 | if (n_args > 0) { |
| 136 | assert(MP_OBJ_IS_SMALL_INT(args[0])); |
| 137 | family = MP_OBJ_SMALL_INT_VALUE(args[0]); |
| 138 | if (n_args > 1) { |
| 139 | assert(MP_OBJ_IS_SMALL_INT(args[1])); |
| 140 | type = MP_OBJ_SMALL_INT_VALUE(args[1]); |
| 141 | if (n_args > 2) { |
| 142 | assert(MP_OBJ_IS_SMALL_INT(args[2])); |
| 143 | proto = MP_OBJ_SMALL_INT_VALUE(args[2]); |
| 144 | } |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | int fd = socket(family, type, proto); |
| 149 | RAISE_ERRNO(fd, errno); |
| 150 | return socket_new(fd); |
| 151 | } |
| 152 | |
| 153 | static const mp_method_t rawsocket_type_methods[] = { |
| 154 | { "read", &mp_stream_read_obj }, |
| 155 | { "readall", &mp_stream_readall_obj }, |
| 156 | { "readline", &mp_stream_unbuffered_readline_obj}, |
| 157 | { "write", &mp_stream_write_obj }, |
| 158 | { "connect", &socket_connect_obj }, |
| 159 | { "bind", &socket_bind_obj }, |
| 160 | { "listen", &socket_listen_obj }, |
| 161 | { "accept", &socket_accept_obj }, |
| 162 | { "close", &socket_close_obj }, |
| 163 | #if MICROPY_SOCKET_EXTRA |
| 164 | { "recv", &mp_stream_read_obj }, |
| 165 | { "send", &mp_stream_write_obj }, |
| 166 | #endif |
| 167 | { NULL, NULL }, |
| 168 | }; |
| 169 | |
| 170 | static const mp_obj_type_t rawsocket_type = { |
| 171 | { &mp_const_type }, |
| 172 | "socket", |
| 173 | .print = socket_print, |
| 174 | .make_new = socket_make_new, |
| 175 | .getiter = NULL, |
| 176 | .iternext = NULL, |
| 177 | .stream_p = { |
| 178 | .read = socket_read, |
| 179 | .write = socket_write, |
| 180 | }, |
| 181 | .methods = rawsocket_type_methods, |
| 182 | }; |
| 183 | |
| 184 | static mp_obj_t mod_socket_htons(mp_obj_t arg) { |
Damien George | a8a6db2 | 2014-01-18 23:50:12 +0000 | [diff] [blame] | 185 | return MP_OBJ_NEW_SMALL_INT((machine_int_t)htons(MP_OBJ_SMALL_INT_VALUE(arg))); |
Paul Sokolovsky | fc92608 | 2014-01-18 23:47:44 +0200 | [diff] [blame] | 186 | } |
| 187 | static MP_DEFINE_CONST_FUN_OBJ_1(mod_socket_htons_obj, mod_socket_htons); |
| 188 | |
| 189 | static mp_obj_t mod_socket_inet_aton(mp_obj_t arg) { |
| 190 | assert(MP_OBJ_IS_TYPE(arg, &str_type)); |
| 191 | const char *s = qstr_str(mp_obj_str_get(arg)); |
| 192 | struct in_addr addr; |
| 193 | if (!inet_aton(s, &addr)) { |
| 194 | nlr_jump(mp_obj_new_exception_msg(MP_QSTR_OSError, "Invalid IP address")); |
| 195 | } |
| 196 | |
| 197 | return mp_obj_new_int(addr.s_addr); |
| 198 | } |
| 199 | static MP_DEFINE_CONST_FUN_OBJ_1(mod_socket_inet_aton_obj, mod_socket_inet_aton); |
| 200 | |
| 201 | #if MICROPY_SOCKET_EXTRA |
| 202 | static mp_obj_t mod_socket_gethostbyname(mp_obj_t arg) { |
| 203 | assert(MP_OBJ_IS_TYPE(arg, &str_type)); |
| 204 | const char *s = qstr_str(mp_obj_str_get(arg)); |
| 205 | struct hostent *h = gethostbyname(s); |
| 206 | if (h == NULL) { |
| 207 | nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_OSError, "[Errno %d]", errno)); |
| 208 | } |
| 209 | assert(h->h_length == 4); |
| 210 | return mp_obj_new_int(*(int*)*h->h_addr_list); |
| 211 | } |
| 212 | static MP_DEFINE_CONST_FUN_OBJ_1(mod_socket_gethostbyname_obj, mod_socket_gethostbyname); |
| 213 | #endif |
| 214 | |
Damien George | a11ceca | 2014-01-19 16:02:09 +0000 | [diff] [blame] | 215 | static mp_obj_t mod_socket_getaddrinfo(uint n_args, const mp_obj_t *args) { |
Paul Sokolovsky | fc92608 | 2014-01-18 23:47:44 +0200 | [diff] [blame] | 216 | // TODO: Implement all args |
| 217 | assert(n_args == 2); |
| 218 | assert(MP_OBJ_IS_TYPE(args[0], &str_type)); |
| 219 | |
| 220 | const char *host = qstr_str(mp_obj_str_get(args[0])); |
| 221 | const char *serv = NULL; |
| 222 | // getaddrinfo accepts port in string notation, so however |
| 223 | // it may seem stupid, we need to convert int to str |
| 224 | if (MP_OBJ_IS_SMALL_INT(args[1])) { |
| 225 | int port = MP_OBJ_SMALL_INT_VALUE(args[1]); |
| 226 | static char buf[20]; |
| 227 | sprintf(buf, "%d", port); |
| 228 | serv = buf; |
| 229 | } else { |
| 230 | serv = qstr_str(mp_obj_str_get(args[1])); |
| 231 | } |
| 232 | |
| 233 | struct addrinfo hints; |
| 234 | struct addrinfo *addr; |
| 235 | memset(&hints, 0, sizeof(hints)); |
| 236 | int res = getaddrinfo(host, serv, NULL/*&hints*/, &addr); |
| 237 | |
| 238 | if (res != 0) { |
| 239 | nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_OSError, "[addrinfo error %d]", res)); |
| 240 | } |
| 241 | assert(addr); |
| 242 | |
| 243 | mp_obj_t list = rt_build_list(0, NULL); |
| 244 | for (; addr; addr = addr->ai_next) { |
| 245 | mp_obj_tuple_t *t = mp_obj_new_tuple(5, NULL); |
Damien George | a8a6db2 | 2014-01-18 23:50:12 +0000 | [diff] [blame] | 246 | t->items[0] = MP_OBJ_NEW_SMALL_INT((machine_int_t)addr->ai_family); |
| 247 | t->items[1] = MP_OBJ_NEW_SMALL_INT((machine_int_t)addr->ai_socktype); |
| 248 | t->items[2] = MP_OBJ_NEW_SMALL_INT((machine_int_t)addr->ai_protocol); |
Paul Sokolovsky | fc92608 | 2014-01-18 23:47:44 +0200 | [diff] [blame] | 249 | // "canonname will be a string representing the canonical name of the host |
| 250 | // if AI_CANONNAME is part of the flags argument; else canonname will be empty." ?? |
| 251 | if (addr->ai_canonname) { |
Damien George | 55baff4 | 2014-01-21 21:40:13 +0000 | [diff] [blame^] | 252 | t->items[3] = MP_OBJ_NEW_QSTR(qstr_from_str(addr->ai_canonname)); |
Paul Sokolovsky | fc92608 | 2014-01-18 23:47:44 +0200 | [diff] [blame] | 253 | } else { |
| 254 | t->items[3] = mp_const_none; |
| 255 | } |
| 256 | t->items[4] = mp_obj_new_bytearray(addr->ai_addrlen, addr->ai_addr); |
| 257 | rt_list_append(list, t); |
| 258 | } |
| 259 | return list; |
| 260 | } |
| 261 | static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_socket_getaddrinfo_obj, 2, 6, mod_socket_getaddrinfo); |
| 262 | |
| 263 | extern mp_obj_type_t sockaddr_in_type; |
| 264 | |
Damien George | 55baff4 | 2014-01-21 21:40:13 +0000 | [diff] [blame^] | 265 | #define STORE_INT_CONST(m, name) rt_store_attr(m, QSTR_FROM_STR_STATIC(#name), MP_OBJ_NEW_SMALL_INT(name)) |
Paul Sokolovsky | fc92608 | 2014-01-18 23:47:44 +0200 | [diff] [blame] | 266 | |
Damien George | a8a6db2 | 2014-01-18 23:50:12 +0000 | [diff] [blame] | 267 | void rawsocket_init() { |
Damien George | 55baff4 | 2014-01-21 21:40:13 +0000 | [diff] [blame^] | 268 | mp_obj_t m = mp_obj_new_module(MP_QSTR_rawsocket); |
| 269 | rt_store_attr(m, MP_QSTR_socket, (mp_obj_t)&rawsocket_type); |
Paul Sokolovsky | fc92608 | 2014-01-18 23:47:44 +0200 | [diff] [blame] | 270 | #if MICROPY_SOCKET_EXTRA |
Damien George | 55baff4 | 2014-01-21 21:40:13 +0000 | [diff] [blame^] | 271 | rt_store_attr(m, MP_QSTR_sockaddr_in, (mp_obj_t)&sockaddr_in_type); |
| 272 | rt_store_attr(m, MP_QSTR_htons, (mp_obj_t)&mod_socket_htons_obj); |
| 273 | rt_store_attr(m, MP_QSTR_inet_aton, (mp_obj_t)&mod_socket_inet_aton_obj); |
| 274 | rt_store_attr(m, MP_QSTR_gethostbyname, (mp_obj_t)&mod_socket_gethostbyname_obj); |
Paul Sokolovsky | fc92608 | 2014-01-18 23:47:44 +0200 | [diff] [blame] | 275 | #endif |
Damien George | 55baff4 | 2014-01-21 21:40:13 +0000 | [diff] [blame^] | 276 | rt_store_attr(m, MP_QSTR_getaddrinfo, (mp_obj_t)&mod_socket_getaddrinfo_obj); |
Paul Sokolovsky | fc92608 | 2014-01-18 23:47:44 +0200 | [diff] [blame] | 277 | STORE_INT_CONST(m, AF_UNIX); |
| 278 | STORE_INT_CONST(m, AF_INET); |
| 279 | STORE_INT_CONST(m, AF_INET6); |
| 280 | STORE_INT_CONST(m, SOCK_STREAM); |
| 281 | STORE_INT_CONST(m, SOCK_DGRAM); |
| 282 | STORE_INT_CONST(m, SOCK_RAW); |
Damien George | 55baff4 | 2014-01-21 21:40:13 +0000 | [diff] [blame^] | 283 | rt_store_name(MP_QSTR_rawsocket, m); |
Paul Sokolovsky | fc92608 | 2014-01-18 23:47:44 +0200 | [diff] [blame] | 284 | } |