|
1 | 1 | #include <sys/stat.h> |
2 | 2 | #include <sys/types.h> |
| 3 | +#include <sys/socket.h> |
3 | 4 | #include <sys/eventfd.h> |
4 | 5 | #include <sys/random.h> |
5 | 6 | #include <sys/syscall.h> |
|
8 | 9 | #include <dirent.h> |
9 | 10 | #include <fcntl.h> |
10 | 11 | #include <unistd.h> |
11 | | - |
| 12 | +#include <netdb.h> |
12 | 13 |
|
13 | 14 | #include <caml/mlvalues.h> |
14 | 15 | #include <caml/memory.h> |
15 | 16 | #include <caml/alloc.h> |
16 | 17 | #include <caml/signals.h> |
17 | 18 | #include <caml/unixsupport.h> |
18 | 19 | #include <caml/bigarray.h> |
| 20 | +#include <caml/socketaddr.h> |
19 | 21 |
|
20 | 22 | // Make sure we have enough space for at least one entry. |
21 | 23 | #define DIRENT_BUF_SIZE (PATH_MAX + sizeof(struct dirent64)) |
@@ -99,3 +101,148 @@ CAMLprim value caml_eio_getdents(value v_fd) { |
99 | 101 |
|
100 | 102 | CAMLreturn(result); |
101 | 103 | } |
| 104 | + |
| 105 | +static value caml_unix_cst_to_constr(int n, int *tbl, int size, int deflt) |
| 106 | +{ |
| 107 | + int i; |
| 108 | + for (i = 0; i < size; i++) |
| 109 | + if (n == tbl[i]) return Val_int(i); |
| 110 | + return Val_int(deflt); |
| 111 | +} |
| 112 | + |
| 113 | +extern int caml_unix_socket_domain_table[]; /* from socket.c */ |
| 114 | +extern int caml_unix_socket_type_table[]; /* from socket.c */ |
| 115 | + |
| 116 | +static value convert_addrinfo(struct addrinfo * a) |
| 117 | +{ |
| 118 | + CAMLparam0(); |
| 119 | + CAMLlocal3(vres,vaddr,vcanonname); |
| 120 | + union sock_addr_union sa; |
| 121 | + socklen_param_type len; |
| 122 | + |
| 123 | + len = a->ai_addrlen; |
| 124 | + if (len > sizeof(sa)) len = sizeof(sa); |
| 125 | + memcpy(&sa.s_gen, a->ai_addr, len); |
| 126 | + vaddr = caml_unix_alloc_sockaddr(&sa, len, -1); |
| 127 | + vcanonname = caml_copy_string(a->ai_canonname == NULL ? "" : a->ai_canonname); |
| 128 | + vres = caml_alloc_small(5, 0); |
| 129 | + Field(vres, 0) = |
| 130 | + caml_unix_cst_to_constr(a->ai_family, caml_unix_socket_domain_table, 3, 0); |
| 131 | + Field(vres, 1) = |
| 132 | + caml_unix_cst_to_constr(a->ai_socktype, caml_unix_socket_type_table, 4, 0); |
| 133 | + Field(vres, 2) = Val_int(a->ai_protocol); |
| 134 | + Field(vres, 3) = vaddr; |
| 135 | + Field(vres, 4) = vcanonname; |
| 136 | + CAMLreturn(vres); |
| 137 | +} |
| 138 | + |
| 139 | +/* glibc doesn't define a bunch of EAI_, so fake one since code gets copied around */ |
| 140 | + |
| 141 | +#ifndef EAI_ADDRFAMILY |
| 142 | +#define EAI_ADDRFAMILY -3000 |
| 143 | +#endif /* EAI_ADDRFAMILY */ |
| 144 | + |
| 145 | +#ifndef EAI_BADHINTS |
| 146 | +#define EAI_BADHINTS -3013 |
| 147 | +#endif /* EAI_BADHINTS */ |
| 148 | + |
| 149 | +#ifndef EAI_NODATA |
| 150 | +#define EAI_NODATA -3007 |
| 151 | +#endif /* EAI_NODATA */ |
| 152 | + |
| 153 | +static int gai_errors[] = { |
| 154 | + EAI_ADDRFAMILY, |
| 155 | + EAI_AGAIN, |
| 156 | + EAI_BADFLAGS, |
| 157 | + EAI_BADHINTS, |
| 158 | + EAI_FAIL, |
| 159 | + EAI_FAMILY, |
| 160 | + EAI_MEMORY, |
| 161 | + EAI_NODATA, |
| 162 | + EAI_NONAME, |
| 163 | + EAI_SERVICE, |
| 164 | + EAI_SOCKTYPE, |
| 165 | + EAI_SYSTEM |
| 166 | +}; |
| 167 | + |
| 168 | +CAMLprim value caml_eio_getaddrinfo(value vnode, value vserv, value vopts) |
| 169 | +{ |
| 170 | + CAMLparam3(vnode, vserv, vopts); |
| 171 | + CAMLlocal3(vres, v, vret); |
| 172 | + char * node, * serv; |
| 173 | + struct addrinfo hints; |
| 174 | + struct addrinfo * res, * r; |
| 175 | + int retcode, i; |
| 176 | + |
| 177 | + if (! (caml_string_is_c_safe(vnode) && caml_string_is_c_safe(vserv))) |
| 178 | + CAMLreturn (Val_emptylist); |
| 179 | + |
| 180 | + /* Extract "node" parameter */ |
| 181 | + if (caml_string_length(vnode) == 0) { |
| 182 | + node = NULL; |
| 183 | + } else { |
| 184 | + node = caml_stat_strdup(String_val(vnode)); |
| 185 | + } |
| 186 | + /* Extract "service" parameter */ |
| 187 | + if (caml_string_length(vserv) == 0) { |
| 188 | + serv = NULL; |
| 189 | + } else { |
| 190 | + serv = caml_stat_strdup(String_val(vserv)); |
| 191 | + } |
| 192 | + /* Parse options, set hints */ |
| 193 | + memset(&hints, 0, sizeof(hints)); |
| 194 | + hints.ai_family = PF_UNSPEC; |
| 195 | + for (/*nothing*/; vopts != Val_emptylist; vopts = Field(vopts, 1)) { |
| 196 | + v = Field(vopts, 0); |
| 197 | + if (Is_block(v)) |
| 198 | + switch (Tag_val(v)) { |
| 199 | + case 0: /* AI_FAMILY of socket_domain */ |
| 200 | + hints.ai_family = caml_unix_socket_domain_table[Int_val(Field(v, 0))]; |
| 201 | + break; |
| 202 | + case 1: /* AI_SOCKTYPE of socket_type */ |
| 203 | + hints.ai_socktype = caml_unix_socket_type_table[Int_val(Field(v, 0))]; |
| 204 | + break; |
| 205 | + case 2: /* AI_PROTOCOL of int */ |
| 206 | + hints.ai_protocol = Int_val(Field(v, 0)); |
| 207 | + break; |
| 208 | + } |
| 209 | + else |
| 210 | + switch (Int_val(v)) { |
| 211 | + case 0: /* AI_NUMERICHOST */ |
| 212 | + hints.ai_flags |= AI_NUMERICHOST; break; |
| 213 | + case 1: /* AI_CANONNAME */ |
| 214 | + hints.ai_flags |= AI_CANONNAME; break; |
| 215 | + case 2: /* AI_PASSIVE */ |
| 216 | + hints.ai_flags |= AI_PASSIVE; break; |
| 217 | + } |
| 218 | + } |
| 219 | + /* Do the call */ |
| 220 | + caml_enter_blocking_section(); |
| 221 | + retcode = getaddrinfo(node, serv, &hints, &res); |
| 222 | + caml_leave_blocking_section(); |
| 223 | + if (node != NULL) caml_stat_free(node); |
| 224 | + if (serv != NULL) caml_stat_free(serv); |
| 225 | + /* Convert result */ |
| 226 | + vres = Val_emptylist; |
| 227 | + if (retcode == 0) { |
| 228 | + for (r = res; r != NULL; r = r->ai_next) { |
| 229 | + v = caml_alloc_small(2, Tag_cons); |
| 230 | + Field(v, 0) = convert_addrinfo(r); |
| 231 | + Field(v, 1) = vres; |
| 232 | + vres = v; |
| 233 | + } |
| 234 | + vret = caml_alloc_small(1, 0); /* 0 = Ok */ |
| 235 | + Field(vret, 0) = vres; |
| 236 | + freeaddrinfo(res); |
| 237 | + } else { |
| 238 | + for (i = 0; i < (sizeof(gai_errors) / sizeof(int)); i++) |
| 239 | + if (gai_errors[i] == retcode) |
| 240 | + break; |
| 241 | + if (i == (sizeof(gai_errors) / sizeof(int))) |
| 242 | + uerror("invalid gai_error", Nothing); |
| 243 | + vret = caml_alloc_small(1, 1); /* 1 = Error */ |
| 244 | + Field(vret, 0) = Val_int(i); |
| 245 | + } |
| 246 | + |
| 247 | + CAMLreturn(vret); |
| 248 | +} |
0 commit comments