Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Allocate pointers to buffers for ioctl requests

Some ioctl requests require a pointer to a user allocated buffer. For
example:

struct  iw_point
{
    void *pointer;
    u_int16_t length;
    u_int16_t flags;
};

Would be constructed using:

{ok, Structure, [Resource]} = procket:alloc([
    {ptr, 4096},                                % buffer
    <<4096:2/unsigned-native-integer-unit:8,    % length
    0:2/native-unsigned-integer-unit:8>>        % flags
]).

A tuple containing the structure and a list of resources (one for
each allocated buffer) is returned.

After calling the ioctl, the contents of the buffer is retrieved by
using the buf/1 function on the resource.

NOTE: If the pointer points to somewhere invalid, ioctl will return
{error, efault}, but if the pointer points somewhere within beam's memory
space, the VM may crash.
  • Loading branch information...
commit 61b1302807e7aa659016d93fce940b0625ba8708 1 parent f4954bc
Michael Santos authored

Showing 2 changed files with 161 additions and 2 deletions. Show diff stats Hide diff stats

  1. +150 1 c_src/procket.c
  2. +11 1 src/procket.erl
151 c_src/procket.c
@@ -37,11 +37,19 @@
37 37 #define BACKLOG 5
38 38
39 39 static ERL_NIF_TERM error_tuple(ErlNifEnv *env, int errnum);
  40 +void alloc_free(ErlNifEnv *env, void *obj);
40 41
41 42 static ERL_NIF_TERM atom_ok;
42 43 static ERL_NIF_TERM atom_error;
43 44 static ERL_NIF_TERM atom_eagain;
44 45
  46 +static ErlNifResourceType *PROCKET_ALLOC_RESOURCE;
  47 +
  48 +typedef struct _alloc_state {
  49 + size_t size;
  50 + void *buf;
  51 +} ALLOC_STATE;
  52 +
45 53
46 54 static int
47 55 load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
@@ -50,6 +58,11 @@ load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
50 58 atom_error = enif_make_atom(env, "error");
51 59 atom_eagain = enif_make_atom(env, "eagain");
52 60
  61 + if ( (PROCKET_ALLOC_RESOURCE = enif_open_resource_type(env, NULL,
  62 + "procket_alloc_resource", alloc_free,
  63 + ERL_NIF_RT_CREATE, NULL)) == NULL)
  64 + return -1;
  65 +
53 66 return (0);
54 67 }
55 68
@@ -380,6 +393,127 @@ nif_setsockopt(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
380 393 }
381 394
382 395
  396 +/* Allocate structures for ioctl
  397 + *
  398 + * Some ioctl request structures have a field pointing
  399 + * to a user allocated buffer.
  400 + */
  401 +
  402 +/* 0: list */
  403 + static ERL_NIF_TERM
  404 +nif_alloc(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
  405 +{
  406 + ERL_NIF_TERM head;
  407 + ERL_NIF_TERM tail;
  408 +
  409 + int arity = 0;
  410 + char key[MAXATOMLEN+1]; /* Includes terminating NULL */
  411 + size_t val = 0;
  412 + const ERL_NIF_TERM *array = NULL;
  413 +
  414 + ERL_NIF_TERM resources = {0};
  415 + ErlNifBinary req = {0};
  416 +
  417 +
  418 + if (!enif_is_list(env, argv[0]) || enif_is_empty_list(env, argv[0]))
  419 + return enif_make_badarg(env);
  420 +
  421 + resources = enif_make_list(env, 0);
  422 + if (!enif_alloc_binary(0, &req))
  423 + return error_tuple(env, ENOMEM);
  424 +
  425 + tail = argv[0];
  426 +
  427 + /* [binary(), {ptr, integer()}, ...] */
  428 + while (enif_get_list_cell(env, tail, &head, &tail)) {
  429 + int index = req.size;
  430 + ErlNifBinary bin = {0};
  431 +
  432 + if (enif_inspect_binary(env, head, &bin)) {
  433 + enif_realloc_binary(&req, req.size+bin.size);
  434 + (void)memcpy(req.data+index, bin.data, bin.size);
  435 + }
  436 + else if (enif_get_tuple(env, head, &arity, &array)) {
  437 + ALLOC_STATE *p = NULL;
  438 + ERL_NIF_TERM res = {0};
  439 +
  440 + if ( (arity != 2) ||
  441 + !enif_get_atom(env, array[0], key, sizeof(key), ERL_NIF_LATIN1) ||
  442 + (strcmp(key, "ptr") != 0) ||
  443 + !enif_get_ulong(env, array[1], (ulong *)&val) ||
  444 + val == 0)
  445 + return enif_make_badarg(env);
  446 +
  447 + p = enif_alloc_resource(PROCKET_ALLOC_RESOURCE, sizeof(ALLOC_STATE));
  448 +
  449 + if (p == NULL)
  450 + return error_tuple(env, ENOMEM);
  451 +
  452 + p->size = val;
  453 + p->buf = calloc(val, 1);
  454 +
  455 + if (p->buf == NULL) {
  456 + enif_release_resource(p);
  457 + return error_tuple(env, ENOMEM);
  458 + }
  459 +
  460 + enif_realloc_binary(&req, req.size+sizeof(void *));
  461 + (void)memcpy(req.data+index, &p->buf, sizeof(void *));
  462 +
  463 + res = enif_make_resource(env, p);
  464 + enif_release_resource(p);
  465 +
  466 + resources = enif_make_list_cell(env, res, resources);
  467 + }
  468 + else
  469 + return enif_make_badarg(env);
  470 + }
  471 +
  472 + return enif_make_tuple3(env, atom_ok,
  473 + enif_make_binary(env, &req),
  474 + resources);
  475 +}
  476 +
  477 +/* 1: resource */
  478 + static ERL_NIF_TERM
  479 +nif_buf(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
  480 +{
  481 + ALLOC_STATE *p = NULL;
  482 +
  483 + ErlNifBinary buf = {0};
  484 +
  485 + if (!enif_get_resource(env, argv[0], PROCKET_ALLOC_RESOURCE, (void **)&p))
  486 + return enif_make_badarg(env);
  487 +
  488 + if (!enif_alloc_binary(p->size, &buf))
  489 + return error_tuple(env, ENOMEM);
  490 +
  491 + (void)memcpy(buf.data, p->buf, buf.size);
  492 +
  493 + return enif_make_tuple2(env,
  494 + atom_ok,
  495 + enif_make_binary(env, &buf));
  496 +}
  497 +
  498 +/* 0: resource, 1: binary */
  499 + static ERL_NIF_TERM
  500 +nif_memcpy(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
  501 +{
  502 + ALLOC_STATE *p = NULL;
  503 + ErlNifBinary buf = {0};
  504 +
  505 +
  506 + if (!enif_get_resource(env, argv[0], PROCKET_ALLOC_RESOURCE, (void **)&p))
  507 + return enif_make_badarg(env);
  508 +
  509 + if (!enif_inspect_binary(env, argv[1], &buf) || buf.size > p->size)
  510 + return enif_make_badarg(env);
  511 +
  512 + (void)memcpy(p->buf, buf.data, p->size);
  513 +
  514 + return atom_ok;
  515 +}
  516 +
383 517 static ERL_NIF_TERM
384 518 error_tuple(ErlNifEnv *env, int errnum)
385 519 {
@@ -389,6 +523,17 @@ error_tuple(ErlNifEnv *env, int errnum)
389 523 }
390 524
391 525
  526 + void
  527 +alloc_free(ErlNifEnv *env, void *obj)
  528 +{
  529 + ALLOC_STATE *p = obj;
  530 +
  531 + if (p->buf)
  532 + free(p->buf);
  533 +}
  534 +
  535 +
  536 +
392 537 static ErlNifFunc nif_funcs[] = {
393 538 {"fdrecv", 1, nif_fdrecv},
394 539
@@ -401,7 +546,11 @@ static ErlNifFunc nif_funcs[] = {
401 546 {"socket_nif", 3, nif_socket},
402 547 {"recvfrom", 4, nif_recvfrom},
403 548 {"sendto", 4, nif_sendto},
404   - {"setsockopt", 4, nif_setsockopt}
  549 + {"setsockopt", 4, nif_setsockopt},
  550 +
  551 + {"alloc", 1, nif_alloc},
  552 + {"memcpy", 2, nif_memcpy},
  553 + {"buf", 1, nif_buf}
405 554 };
406 555
407 556 ERL_NIF_INIT(procket, nif_funcs, load, NULL, NULL, NULL)
12 src/procket.erl
@@ -43,7 +43,11 @@
43 43 sendto/2, sendto/3,sendto/4,
44 44 bind/2,
45 45 ioctl/3,
46   - setsockopt/4
  46 + setsockopt/4,
  47 +
  48 + alloc/1,
  49 + buf/1,
  50 + memcpy/2
47 51 ]).
48 52 -export([unix_path_max/0, sockaddr_common/2]).
49 53 -export([make_args/2,progname/0]).
@@ -107,6 +111,12 @@ socket_nif(_,_,_) ->
107 111
108 112 ioctl(_,_,_) ->
109 113 erlang:error(not_implemented).
  114 +alloc(_) ->
  115 + erlang:error(not_implemented).
  116 +buf(_) ->
  117 + erlang:error(not_implemented).
  118 +memcpy(_,_) ->
  119 + erlang:error(not_implemented).
110 120
111 121 sendto(Socket, Buf) ->
112 122 sendto(Socket, Buf, 0, <<>>).

0 comments on commit 61b1302

Please sign in to comment.
Something went wrong with that request. Please try again.