UDP+binary+passive and prim_inet:recvfrom/3 bug?

Tony Rogvall tony@REDACTED
Mon Jan 8 00:01:36 CET 2001


Samuel Tardieu wrote:

> On  5/01, Tony Rogvall wrote:
>
> | From inet_drv.c
> |
> | /*
> | ** passive mode reply:
> | **        {inet_async, S, Ref, {ok,[H1,...Hsz | Data]}}
> | */
> |
> | This means that the inet_drv ADDs a header list of sz bytes. In this case
> | the header data is the Familiy, Port and Address, the rest my or may  not be a
> | binary.
>
> However, the following code:
>
> -module (bug).
> -export ([start_udp/0]).
>
> start_udp () ->
>   {ok, U} = gen_udp:open (4161, [binary, {active, false}]),
>   io:format ("Received: ~p~n", [prim_inet:recvfrom (U, 1500)]),
>   erlang:halt ().
>
> gives, when a UDP packet is received on port 4161:
>
> {ok,<<1,12,73,127,0,0,1,0,6,1,0,0,1,0,0,0,0,0,0,6,102,111,111,98,97,114,0,0,1,0,1>>}
>
> If the binary option is removed, it gives:
>
> {ok,{{127,0,0,1},
>      3154,
>      [0,6,1,0,0,1,0,0,0,0,0,0,6,102,111,111,98,97,114,0,0,1,0,1]}}

Yes you are absolutely right, by demonstration :-)

If you need a quick fix you can update the inet_drv as follows:
(I can not produce a clean patch right now, I hope some one at OTP team can do it for
us)

for simplicity replace the following functions (a proper emacs mode will fix the looks
of this)

/Tony

------------------START CUTTING AND PASTING ----------------------------

static int inet_async_binary_data(desc, phsz, bin, offs, len)
inet_descriptor* desc; unsigned int phsz;
DriverBinary* bin; int offs; int len;
{
    unsigned int hsz = desc->hsz + phsz;
    DriverTermData spec[20];
    DriverTermData caller = desc->caller;
    int aid;
    int req;
    int i = 0;

    DEBUGF(("inet_async_binary_data(%d): offs=%d, len = %d\r\n",
     desc->port, offs, len));

    if (deq_async(desc, &aid, &caller, &req) < 0)
 return -1;

    i = LOAD_ATOM(spec, i, am_inet_async);
    i = LOAD_PORT(spec, i, desc->dport);
    i = LOAD_INT(spec, i,  aid);

    i = LOAD_ATOM(spec, i, am_ok);

    if ((desc->mode == INET_MODE_LIST) || (hsz > len)) {
 /* INET_MODE_LIST => [H1,H2,...Hn] */
 i = LOAD_STRING(spec, i, bin->orig_bytes+offs, len);
    }
    else {
 /* INET_MODE_BINARY => [H1,H2,...HSz | Binary] */
 int sz = len - hsz;
 i = LOAD_BINARY(spec, i, bin, offs+hsz, sz);
 if (hsz > 0)
     i = LOAD_STRING_CONS(spec, i, bin->orig_bytes+offs, hsz);
    }
    i = LOAD_TUPLE(spec, i, 2);
    i = LOAD_TUPLE(spec, i, 4);
    ASSERT(i <= 20);
    desc->caller = 0;
    return driver_send_term(desc->port, caller, spec, i);
}


static int tcp_reply_binary_data(desc, bin, offs, len)
tcp_descriptor* desc;  DriverBinary* bin; int offs; int len;
{
    int code;

    /* adjust according to packet type */
    switch(desc->inet.htype) {
    case TCP_PB_1:  offs += 1; len -= 1; break;
    case TCP_PB_2:  offs += 2; len -= 2; break;
    case TCP_PB_4:  offs += 4; len -= 4; break;
    case TCP_PB_FCGI:
 len -= ((struct fcgi_head*)(bin->orig_bytes+offs))->paddingLength;
 break;
    }

    SCANBIT8(INETP(desc), bin->orig_bytes+offs, len);

    if (desc->inet.deliver == INET_DELIVER_PORT)
        code = inet_port_binary_data(INETP(desc), bin, offs, len);
#ifdef USE_HTTP
    else if (desc->inet.htype == TCP_PB_HTTP) {
        if ((code = http_message(desc, bin->orig_bytes+offs, len)) < 0)
     http_error_message(desc, bin->orig_bytes+offs, len);
 code = 0;
    }
#endif
    else if (desc->inet.active == INET_PASSIVE)
      return inet_async_binary_data(INETP(desc), 0, bin, offs, len);
    else
 code = tcp_binary_message(desc, bin, offs, len);
    if (code < 0)
 return code;
    if (desc->inet.active == INET_ONCE)
 desc->inet.active = INET_PASSIVE;
    return code;
}


static int udp_reply_binary_data(desc, hsz, bin, offs, len)
inet_descriptor* desc; unsigned int hsz; DriverBinary* bin; int offs; int len;
{
    int code;

    SCANBIT8(desc, bin->orig_bytes+offs, len);

    if (desc->active == INET_PASSIVE)
 return inet_async_binary_data(desc, hsz, bin, offs, len);
    else if (desc->deliver == INET_DELIVER_PORT)
 code = inet_port_binary_data(desc, bin, offs, len);
    else
 code = udp_binary_message(desc, bin, offs, len);
    if (code < 0)
 return code;
    if (desc->active == INET_ONCE)
 desc->active = INET_PASSIVE;
    return code;
}

static int udp_inet_input(desc, event)
udp_descriptor* desc; HANDLE event;
{
    int n;
    int len;
    inet_address other;
    char abuf[sizeof(inet_address)];  /* buffer address */
    int sz;
    char* ptr;
    DriverBinary* buf; /* binary */
    int packet_count = INET_UDP_POLL;
    int count = 0;   /* number of packets delivered to owner */

    while(packet_count--) {
 len = sizeof(other);
 sz = desc->inet.bufsz;
 /* Allocate space for message and address */
 if ((buf = alloc_buffer(sz+len)) == NULL)
     return udp_error(desc, ENOMEM);
 ptr = buf->orig_bytes + len;  /* point to message part */

 /* Note: On Windows NT, recvfrom() fails if the socket is connected. */
 if (desc->inet.state & INET_F_ACTIVE) {
     n = sock_recv(desc->inet.s, ptr, sz, 0);
     other = desc->inet.remote;
 }
 else
     n = sock_recvfrom(desc->inet.s, ptr, sz, 0,
         (struct sockaddr*)&other, &len);
 if (n == SOCKET_ERROR) {
     int err = sock_errno();
     release_buffer(buf);
     if (err != ERRNO_BLOCK) {
  if (!desc->inet.active) {
      async_error(desc, err);
      driver_cancel_timer(desc->inet.port);
      sock_select(INETP(desc),FD_READ,0);
  }
  else {
      udp_error_message(desc, err);
  }
     }
     else if (!desc->inet.active)
  sock_select(INETP(desc),FD_READ,1);
     return count;  /* strange, not ready */
 }
 else {
     int offs;
     int nsz;
     int code;

     inet_input_count(INETP(desc), n);

     inet_get_address(desc->inet.sfamily, abuf, &other, &len);

     /* copy formatted address to ptr len is actual length */
     sys_memcpy(ptr - len, abuf, len);
     ptr -= len;
     nsz = n + len;                /* nsz = data + address */
     offs = ptr - buf->orig_bytes; /* initial pointer offset */

     /* check if we need to reallocate binary */
     if ((desc->inet.mode == INET_MODE_BINARY) &&
  (desc->inet.hsz < n) && (nsz < BIN_REALLOC_LIMIT(sz))) {
  DriverBinary* tmp;
  if ((tmp = realloc_buffer(buf,nsz+offs)) != NULL)
      buf = tmp;
     }
     code = udp_reply_binary_data(desc,(unsigned int)len,buf,offs,nsz);
     free_buffer(buf);
     if (code < 0)
  return count;
     count++;
     if (!desc->inet.active) {
  driver_cancel_timer(desc->inet.port); /* possibly cancel */
  sock_select(INETP(desc),FD_READ,0);
  return count;  /* passive mode (read one packet only) */
     }
 }
    }
    return count;
}

---------------------- YOU CAN STOP NOW -------------------------------------


-------------- next part --------------
A non-text attachment was scrubbed...
Name: tony.vcf
Type: text/x-vcard
Size: 319 bytes
Desc: Card for Tony Rogvall
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20010108/9aa7d6e8/attachment.vcf>


More information about the erlang-questions mailing list