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

Raimo Niskanen <>
Tue Jan 9 08:44:10 CET 2001


I will affect these changes to OTP R7B, include them in a patch, and get
back with more info about which patch.

/ Raimo Niskanen, Ericsson UAB, Erlang/OTP




Tony Rogvall wrote:
> 
> 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 -------------------------------------



More information about the erlang-questions mailing list