/* 0: socket, 1: length, 2: flags, 3: struct sockaddr length */ static ERL_NIF_TERM nif_recvfrom(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { int sockfd = -1; unsigned long len = 0; unsigned long salen = 0; int flags = 0; ErlNifBinary buf = {0}; ErlNifBinary sa = {0}; ssize_t bufsz = 0; if (!enif_get_int(env, argv[0], &sockfd)) return enif_make_badarg(env); if (!enif_get_ulong(env, argv[1], &len)) return enif_make_badarg(env); if (!enif_get_int(env, argv[2], &flags)) return enif_make_badarg(env); if (!enif_get_ulong(env, argv[3], &salen)) return enif_make_badarg(env); if (!enif_alloc_binary(len, &buf)) return error_tuple(env, ENOMEM); if (!enif_alloc_binary(salen, &sa)) return error_tuple(env, ENOMEM); if ( (bufsz = recvfrom(sockfd, buf.data, buf.size, flags, (sa.size == 0 ? NULL : (struct sockaddr *)sa.data), (socklen_t *)&salen)) == -1) { enif_release_binary(&buf); enif_release_binary(&sa); switch (errno) { case EAGAIN: case EINTR: return enif_make_tuple2(env, atom_error, atom_eagain); default: return error_tuple(env, errno); } } PROCKET_REALLOC(buf, bufsz); PROCKET_REALLOC(sa, salen); return enif_make_tuple3(env, atom_ok, enif_make_binary(env, &buf), enif_make_binary(env, &sa)); }
// int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen); static ERL_NIF_TERM nif_getsockname(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { int s = -1; ErlNifBinary addr = {0}; socklen_t addrlen = 0; if (!enif_get_int(env, argv[0], &s)) return enif_make_badarg(env); if (!enif_inspect_binary(env, argv[1], &addr)) return enif_make_badarg(env); /* Make the binary mutable */ if (!enif_realloc_binary(&addr, addr.size)) return error_tuple(env, ENOMEM); addrlen = addr.size; if (getsockname(s, (struct sockaddr *)addr.data, (socklen_t *)&addrlen) < 0) return error_tuple(env, errno); PROCKET_REALLOC(addr, addrlen); return enif_make_tuple2(env, atom_ok, enif_make_binary(env, &addr)); }
/* 0: socket, 1: length */ static ERL_NIF_TERM nif_read(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { int fd = -1; unsigned long len = 0; ErlNifBinary buf = {0}; ssize_t bufsz = 0; if (!enif_get_int(env, argv[0], &fd)) return enif_make_badarg(env); if (!enif_get_ulong(env, argv[1], &len)) return enif_make_badarg(env); if (!enif_alloc_binary(len, &buf)) return error_tuple(env, ENOMEM); if ( (bufsz = read(fd, buf.data, buf.size)) == -1) { int err = errno; enif_release_binary(&buf); switch (err) { case EAGAIN: case EINTR: return enif_make_tuple2(env, atom_error, atom_eagain); default: return error_tuple(env, err); } } PROCKET_REALLOC(buf, bufsz); return enif_make_tuple2(env, atom_ok, enif_make_binary(env, &buf)); }
static ERL_NIF_TERM nif_setns(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { #ifdef HAVE_SETNS int fd; ErlNifBinary buf = {0}; int nstype = 0; int errnum = 0; if (!enif_inspect_iolist_as_binary(env, argv[0], &buf)) return enif_make_badarg(env); if (!enif_get_int(env, argv[1], &nstype)) return enif_make_badarg(env); PROCKET_REALLOC(buf, buf.size+1); buf.data[buf.size-1] = '\0'; fd = open((const char*)buf.data, O_RDONLY); /* Get descriptor for namespace */ if (fd < 0) return error_tuple(env, errno); if (setns(fd, nstype) == -1) { errnum = errno; (void)close(fd); return error_tuple(env, errnum); } (void)close(fd); return atom_ok; #else return error_tuple(env, ENOTSUP); #endif }
/* 0: socket, 1: struct sockaddr length */ static ERL_NIF_TERM nif_accept(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { int l = -1; int s = -1; unsigned long salen = 0; ErlNifBinary sa = {0}; int flags = 0; if (!enif_get_int(env, argv[0], &l)) return enif_make_badarg(env); if (!enif_get_ulong(env, argv[1], &salen)) return enif_make_badarg(env); if (!enif_alloc_binary(salen, &sa)) return error_tuple(env, ENOMEM); s = accept(l, (sa.size == 0 ? NULL : (struct sockaddr *)sa.data), (socklen_t *)&salen); if (s < 0) return error_tuple(env, errno); flags = fcntl(s, F_GETFL, 0); if (flags < 0) return error_tuple(env, errno); if (fcntl(s, F_SETFL, flags|O_NONBLOCK) < 0) return error_tuple(env, errno); PROCKET_REALLOC(sa, salen); return enif_make_tuple3(env, atom_ok, enif_make_int(env, s), enif_make_binary(env, &sa)); }
/* 0: int socket descriptor, 1: int level, * 2: int optname, 3: void *optval */ static ERL_NIF_TERM nif_getsockopt(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { int s = -1; int level = 0; int optname = 0; ErlNifBinary optval = {0}; socklen_t optlen = 0; if (!enif_get_int(env, argv[0], &s)) return enif_make_badarg(env); if (!enif_get_int(env, argv[1], &level)) return enif_make_badarg(env); if (!enif_get_int(env, argv[2], &optname)) return enif_make_badarg(env); if (!enif_inspect_binary(env, argv[3], &optval)) return enif_make_badarg(env); /* Make the binary mutable */ if (!enif_realloc_binary(&optval, optval.size)) return error_tuple(env, ENOMEM); optlen = optval.size; if (getsockopt(s, level, optname, (optval.size == 0 ? NULL : optval.data), &optlen) < 0) return error_tuple(env, errno); PROCKET_REALLOC(optval, optlen); return enif_make_tuple2(env, atom_ok, enif_make_binary(env, &optval)); }
static ERL_NIF_TERM nif_recvmsg(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { int s = -1; ErlNifBinary buf = {0}; ErlNifBinary src_addr = {0}; char *ctrldata = NULL; ERL_NIF_TERM ctrldatalist; int flags = 0; unsigned long bufsize = 0; unsigned long ctrlsize = 0; unsigned long sasize = 0; struct iovec iov[1]; struct msghdr message; struct cmsghdr *cmsg; ssize_t n = 0; if (!enif_get_int(env, argv[0], &s)) return enif_make_badarg(env); if (!enif_get_ulong(env, argv[1], &bufsize)) return enif_make_badarg(env); if (!enif_get_int(env, argv[2], &flags)) return enif_make_badarg(env); if (!enif_get_ulong(env, argv[3], &ctrlsize)) return enif_make_badarg(env); if (!enif_get_ulong(env, argv[4], &sasize)) return enif_make_badarg(env); if (!enif_alloc_binary(bufsize, &buf)) return error_tuple(env, ENOMEM); if (ctrlsize > 0 && !(ctrldata = malloc(ctrlsize))) { enif_release_binary(&buf); return error_tuple(env, ENOMEM); } if (!enif_alloc_binary(sasize, &src_addr)) { enif_release_binary(&buf); free(ctrldata); return error_tuple(env, ENOMEM); } iov[0].iov_base = (buf.size == 0 ? NULL : buf.data); iov[0].iov_len=buf.size; message.msg_name = (src_addr.size == 0 ? NULL : src_addr.data); message.msg_namelen=src_addr.size; message.msg_iov=iov; message.msg_iovlen=1; message.msg_control=ctrldata; message.msg_controllen=ctrlsize; n = recvmsg(s, &message, flags); if (n < 0) { int err = errno; enif_release_binary(&buf); enif_release_binary(&src_addr); free(ctrldata); return error_tuple(env, err); } /* resize the binary to the actual size of the received packet * * XXX On error, the macro will return ENOMEM here, leaking buf, * XXX src_addr and ctrldata. Since the VM has OOM'ed, it will probably * XXX crash anyway. */ PROCKET_REALLOC(buf, n); PROCKET_REALLOC(src_addr, message.msg_namelen); ctrldatalist = enif_make_list(env, 0); for (cmsg = CMSG_FIRSTHDR(&message); cmsg != NULL; cmsg = CMSG_NXTHDR(&message, cmsg)) { size_t len = cmsg->cmsg_len - CMSG_LEN(0); ErlNifBinary cdata = {0}; if (!enif_alloc_binary(len, &cdata)) { enif_release_binary(&buf); enif_release_binary(&src_addr); free(ctrldata); return error_tuple(env, ENOMEM); } memcpy(cdata.data, CMSG_DATA(cmsg), len); ctrldatalist = enif_make_list_cell(env, enif_make_tuple3(env, enif_make_int(env, cmsg->cmsg_level), enif_make_int(env, cmsg->cmsg_type), enif_make_binary(env, &cdata)), ctrldatalist); } free(ctrldata); return enif_make_tuple5(env, atom_ok, enif_make_binary(env, &buf), /* Data packet */ enif_make_int(env, message.msg_flags), /* the message flags, eg. MSG_EOR, MSG_OOB, etc. */ ctrldatalist, /* array of 3-tuples of {cmsg->cmsg_level, cmsg->cmsg_type, cmsg->cmsg_data} */ enif_make_binary(env, &src_addr) /* source address, as a sockaddr_storage */ ); }