Ejemplo n.º 1
0
/**
 * Attach the current thread to the UDP Socket
 *
 * @param us UDP Socket
 *
 * @return 0 if success, otherwise errorcode
 */
int udp_thread_attach(struct udp_sock *us)
{
	int err = 0;

	if (!us)
		return EINVAL;

	if (-1 != us->fd) {
		err = fd_listen(us->fd, FD_READ, udp_read_handler, us);
		if (err)
			goto out;
	}

	if (-1 != us->fd6) {
		err = fd_listen(us->fd6, FD_READ, udp_read_handler6, us);
		if (err)
			goto out;
	}

 out:
	if (err)
		udp_thread_detach(us);

	return err;
}
Ejemplo n.º 2
0
static int enqueue(struct tcp_conn *tc, struct mbuf *mb)
{
	const size_t n = mbuf_get_left(mb);
	struct tcp_qent *qe;
	int err;

	if (tc->txqsz + n > tc->txqsz_max)
		return ENOSPC;

	if (!tc->sendq.head && !tc->sendh) {

		err = fd_listen(tc->fdc, FD_READ | FD_WRITE,
				tcp_recv_handler, tc);
		if (err)
			return err;
	}

	qe = mem_zalloc(sizeof(*qe), qent_destructor);
	if (!qe)
		return ENOMEM;

	list_append(&tc->sendq, &qe->le, qe);

	mbuf_init(&qe->mb);

	err = mbuf_write_mem(&qe->mb, mbuf_buf(mb), n);
	qe->mb.pos = 0;

	if (err)
		mem_deref(qe);
	else
		tc->txqsz += qe->mb.end;

	return err;
}
Ejemplo n.º 3
0
/**
 * Accept an incoming TCP Connection
 *
 * @param tcp Returned TCP Connection object
 * @param ts  Corresponding TCP Socket
 * @param eh  TCP Connection Established handler
 * @param rh  TCP Connection Receive data handler
 * @param ch  TCP Connection close handler
 * @param arg Handler argument
 *
 * @return 0 if success, otherwise errorcode
 */
int tcp_accept(struct tcp_conn **tcp, struct tcp_sock *ts, tcp_estab_h *eh,
	       tcp_recv_h *rh, tcp_close_h *ch, void *arg)
{
	struct tcp_conn *tc;
	int err;

	if (!tcp || !ts || ts->fdc < 0)
		return EINVAL;

	tc = conn_alloc(eh, rh, ch, arg);
	if (!tc)
		return ENOMEM;

	/* Transfer ownership to TCP connection */
	tc->fdc = ts->fdc;
	ts->fdc = -1;

	err = fd_listen(tc->fdc, FD_READ | FD_WRITE | FD_EXCEPT,
			tcp_recv_handler, tc);
	if (err) {
		DEBUG_WARNING("accept: fd_listen(): %m\n", err);
	}

	if (err)
		mem_deref(tc);
	else
		*tcp = tc;

	return err;
}
Ejemplo n.º 4
0
/*! @decl void create(int|string port, void|function accept_callback, @
 *!                   void|string ip)
 *! @decl void create("stdin", void|function accept_callback)
 *!
 *! When called with an int or any string except @expr{"stdin"@} as
 *! first argument, this function does the same as @[bind()] would do
 *! with the same arguments.
 *!
 *! When called with @expr{"stdin"@} as argument, a socket is created
 *! out of the file descriptor 0. This is only useful if that actually
 *! IS a socket to begin with, and is equivalent to creating a port and
 *! initializing it with @[listen_fd](0).
 *!
 *! @seealso
 *!   @[bind], @[listen_fd]
 */
static void port_create(INT32 args)
{
  if(args)
  {
    if(TYPEOF(Pike_sp[-args]) == PIKE_T_INT ||
       (TYPEOF(Pike_sp[-args]) == PIKE_T_STRING &&
	(Pike_sp[-args].u.string->len != 5 ||
	 strcmp("stdin",Pike_sp[-args].u.string->str))))
    {
      port_bind(args);
      return;
    }else{
      struct port *p = THIS;

      if(TYPEOF(Pike_sp[-args]) != PIKE_T_STRING)
	SIMPLE_WRONG_NUM_ARGS_ERROR("create", 1);

      /* FIXME: Check that the argument is "stdin". */

      do_close(p);
      change_fd_for_box (&p->box, 0);

      if(fd_listen(p->box.fd, 16384) < 0)
      {
	p->my_errno=errno;
      }else{
	if(args > 1) assign_accept_cb (p, Pike_sp+1-args);
      }
    }
  }
}
Ejemplo n.º 5
0
static int encode_update(struct videnc_state **vesp, const struct vidcodec *vc,
			 struct videnc_param *prm, const char *fmtp,
			 videnc_packet_h *pkth, void *arg)
{
	struct videnc_state *st;
	int err = 0;
	(void)fmtp;

	if (!vesp || !vc || !prm || !pkth)
		return EINVAL;

	if (*vesp)
		return 0;

	st = mem_zalloc(sizeof(*st), enc_destructor);
	if (!st)
		return ENOMEM;

	st->encprm = *prm;
	st->pkth = pkth;
	st->arg = arg;

	st->fd = open(v4l2.device, O_RDWR);
	if (st->fd == -1) {
		err = errno;
		warning("Opening video device (%m)\n", err);
		goto out;
	}

	err = print_caps(st->fd);
	if (err)
		goto out;

	err = init_mmap(st, st->fd);
	if (err)
		goto out;

	err = query_buffer(st->fd);
	if (err)
		goto out;

	err = start_streaming(st->fd);
	if (err)
		goto out;

	err = fd_listen(st->fd, FD_READ, read_handler, st);
	if (err)
		goto out;

	info("v4l2_codec: video encoder %s: %d fps, %d bit/s, pktsize=%u\n",
	      vc->name, prm->fps, prm->bitrate, prm->pktsize);

 out:
	if (err)
		mem_deref(st);
	else
		*vesp = st;

	return err;
}
Ejemplo n.º 6
0
/*! @decl int listen_fd(int fd, void|function accept_callback)
 *!
 *! This function does the same as @[bind], except that instead of
 *! creating a new socket and bind it to a port, it expects the file
 *! descriptor @[fd] to be an already open port.
 *!
 *! @note
 *!  This function is only for the advanced user, and is generally used
 *!  when sockets are passed to Pike at exec time.
 *!
 *! @seealso
 *!   @[bind], @[accept]
 */
static void port_listen_fd(INT32 args)
{
  struct port *p = THIS;
  struct svalue *cb = NULL;
  int fd;
  do_close(p);

  get_all_args(NULL, args, "%d.%*", &fd, &cb);

  if(fd<0)
  {
    errno = p->my_errno=EBADF;
    pop_n_elems(args);
    push_int(0);
    return;
  }

  if(fd_listen(fd, 16384) < 0)
  {
    p->my_errno=errno;
    pop_n_elems(args);
    push_int(0);
    return;
  }

  change_fd_for_box (&p->box, fd);
  if(cb) assign_accept_cb (p, cb);
  p->my_errno=0;
  pop_n_elems(args);
  push_int(1);
}
Ejemplo n.º 7
0
Archivo: stdio.c Proyecto: GGGO/baresip
static int ui_alloc(struct ui_st **stp)
{
	struct ui_st *st;
	int err;

	if (!stp)
		return EINVAL;

	st = mem_zalloc(sizeof(*st), ui_destructor);
	if (!st)
		return ENOMEM;

	tmr_init(&st->tmr);

	err = fd_listen(STDIN_FILENO, FD_READ, ui_fd_handler, st);
	if (err)
		goto out;

	err = term_setup(st);
	if (err) {
		info("stdio: could not setup terminal: %m\n", err);
		err = 0;
	}

 out:
	if (err)
		mem_deref(st);
	else
		*stp = st;

	return err;
}
Ejemplo n.º 8
0
int sock_listen(net_sock_t *nsock, int max_pending)
{
  network_sock_t *sock = (network_sock_t *)nsock;   

  if (sock == NULL) return(1);

  return(fd_listen(sock->fd, max_pending));
}
Ejemplo n.º 9
0
static int src_alloc(struct ausrc_st **stp, struct ausrc *as,
		     struct media_ctx **ctx,
		     struct ausrc_prm *prm, const char *device,
		     ausrc_read_h *rh, ausrc_error_h *errh, void *arg)
{
	struct ausrc_st *st;
	int err;

	(void)ctx;
	(void)errh;

	st = mem_zalloc(sizeof(*st), ausrc_destructor);
	if (!st)
		return ENOMEM;

	st->fd   = -1;
	st->rh   = rh;
	st->errh = errh;
	st->arg  = arg;

	if (!device)
		device = oss_dev;

	prm->fmt = AUFMT_S16LE;

	st->mb = mbuf_alloc(2 * prm->frame_size);
	if (!st->mb) {
		err = ENOMEM;
		goto out;
	}

	st->fd = open(device, O_RDONLY);
	if (st->fd < 0) {
		err = errno;
		goto out;
	}

	err = fd_listen(st->fd, FD_READ, read_handler, st);
	if (err)
		goto out;

	err = oss_reset(st->fd, prm->srate, prm->ch, prm->frame_size, 1);
	if (err)
		goto out;

	st->as = mem_ref(as);

 out:
	if (err)
		mem_deref(st);
	else
		*stp = st;

	return err;
}
Ejemplo n.º 10
0
/**
 * Set the send handler on a TCP Connection, which will be called
 * every time it is ready to send data
 *
 * @param tc    TCP Connection
 * @param sendh TCP Send handler
 *
 * @return 0 if success, otherwise errorcode
 */
int tcp_set_send(struct tcp_conn *tc, tcp_send_h *sendh)
{
	if (!tc)
		return EINVAL;

	tc->sendh = sendh;

	if (tc->sendq.head || !sendh)
		return 0;

	return fd_listen(tc->fdc, FD_READ | FD_WRITE, tcp_recv_handler, tc);
}
Ejemplo n.º 11
0
/**
 * Listen on a TCP Socket
 *
 * @param ts       TCP Socket
 * @param backlog  Maximum length the queue of pending connections
 *
 * @return 0 if success, otherwise errorcode
 */
int tcp_sock_listen(struct tcp_sock *ts, int backlog)
{
	int err;

	if (!ts)
		return EINVAL;

	if (ts->fd < 0) {
		DEBUG_WARNING("sock_listen: invalid fd\n");
		return EBADF;
	}

	if (listen(ts->fd, backlog) < 0) {
		err = errno;
		DEBUG_WARNING("sock_listen: listen(): %m\n", err);
		return err;
	}

	return fd_listen(ts->fd, FD_READ, tcp_conn_handler, ts);
}
Ejemplo n.º 12
0
/**
 * Allocate a new Message Queue
 *
 * @param mqp Pointer to allocated Message Queue
 * @param h   Message handler
 * @param arg Handler argument
 *
 * @return 0 if success, otherwise errorcode
 */
int mqueue_alloc(struct mqueue **mqp, mqueue_h *h, void *arg)
{
	struct mqueue *mq;
	int err = 0;

	if (!mqp || !h)
		return EINVAL;

	mq = mem_zalloc(sizeof(*mq), destructor);
	if (!mq)
		return ENOMEM;

	mq->h   = h;
	mq->arg = arg;

	mq->pfd[0] = mq->pfd[1] = -1;
	if (pipe(mq->pfd) < 0) {
		err = errno;
		goto out;
	}

	err = net_sockopt_blocking_set(mq->pfd[0], false);
	if (err)
		goto out;

	err = net_sockopt_blocking_set(mq->pfd[1], false);
	if (err)
		goto out;

	err = fd_listen(mq->pfd[0], FD_READ, event_handler, mq);
	if (err)
		goto out;

 out:
	if (err)
		mem_deref(mq);
	else
		*mqp = mq;

	return err;
}
Ejemplo n.º 13
0
/**
 * Handler for incoming TCP connections.
 *
 * @param flags  Event flags.
 * @param arg    Handler argument.
 */
static void tcp_conn_handler(int flags, void *arg)
{
	struct sa peer;
	struct tcp_sock *ts = arg;
	int err;

	(void)flags;

	sa_init(&peer, AF_UNSPEC);

	if (ts->fdc >= 0)
		(void)close(ts->fdc);

	ts->fdc = SOK_CAST accept(ts->fd, &peer.u.sa, &peer.len);
	if (-1 == ts->fdc) {

#if TARGET_OS_IPHONE
		if (EAGAIN == errno) {

			struct tcp_sock *ts_new;
			struct sa laddr;

			err = tcp_sock_local_get(ts, &laddr);
			if (err)
				return;

			if (ts->fd >= 0) {
				fd_close(ts->fd);
				(void)close(ts->fd);
				ts->fd = -1;
			}

			err = tcp_listen(&ts_new, &laddr, NULL, NULL);
			if (err)
				return;

			ts->fd = ts_new->fd;
			ts_new->fd = -1;

			mem_deref(ts_new);

			fd_listen(ts->fd, FD_READ, tcp_conn_handler, ts);
		}
#endif

		return;
	}

	err = net_sockopt_blocking_set(ts->fdc, false);
	if (err) {
		DEBUG_WARNING("conn handler: nonblock set: %m\n", err);
		(void)close(ts->fdc);
		ts->fdc = -1;
		return;
	}

	tcp_sockopt_set(ts->fdc);

	if (ts->connh)
		ts->connh(&peer, ts->arg);
}
Ejemplo n.º 14
0
Archivo: main.c Proyecto: Issic47/libre
/**
 * Stop listening for events on a file descriptor
 *
 * @param fd     File descriptor
 */
void fd_close(int fd)
{
	(void)fd_listen(fd, 0, NULL, NULL);
}
Ejemplo n.º 15
0
/*! @decl int bind(int|string port, void|function accept_callback, @
 *!                void|string ip, void|string reuse_port)
 *!
 *! Opens a socket and binds it to port number on the local machine.
 *! If the second argument is present, the socket is set to
 *! nonblocking and the callback funcition is called whenever
 *! something connects to it. The callback will receive the id for
 *! this port as argument and should typically call @[accept] to
 *! establish a connection.
 *!
 *! If the optional argument @[ip] is given, @[bind] will try to bind
 *! to an interface with that host name or IP number. Omitting this
 *! will bind to all available IPv4 addresses; specifying "::" will
 *! bind to all IPv4 and IPv6 addresses.
 *!
 *! If the OS supports TCP_FASTOPEN it is enabled automatically.
 *!
 *! If the OS supports SO_REUSEPORT it is enabled if the fourth argument is true.
 *!
 *! @returns
 *!   1 is returned on success, zero on failure. @[errno] provides
 *!   further details about the error in the latter case.
 *!
 *! @seealso
 *!   @[accept], @[set_id]
 */
static void port_bind(INT32 args)
{
  struct port *p = THIS;
  PIKE_SOCKADDR addr;
  int addr_len,fd,tmp;

  do_close(p);

  if(args < 1)
    SIMPLE_WRONG_NUM_ARGS_ERROR("bind", 1);

  if(TYPEOF(Pike_sp[-args]) != PIKE_T_INT &&
     (TYPEOF(Pike_sp[-args]) != PIKE_T_STRING ||
      Pike_sp[-args].u.string->size_shift))
    SIMPLE_ARG_TYPE_ERROR("bind", 1, "int|string(8bit)");

  addr_len = get_inet_addr(&addr,
                           (args > 2 && TYPEOF(Pike_sp[2-args])==PIKE_T_STRING?
                            Pike_sp[2-args].u.string->str : NULL),
                           (TYPEOF(Pike_sp[-args]) == PIKE_T_STRING?
                            Pike_sp[-args].u.string->str : NULL),
			   (TYPEOF(Pike_sp[-args]) == PIKE_T_INT?
			    Pike_sp[-args].u.integer : -1), 0);
  INVALIDATE_CURRENT_TIME();

  fd=fd_socket(SOCKADDR_FAMILY(addr), SOCK_STREAM, 0);

  if(fd < 0)
  {
    p->my_errno=errno;
    pop_n_elems(args);
    push_int(0);
    return;
  }
#ifdef SO_REUSEPORT
  if( args > 3 && Pike_sp[3-args].u.integer )
  {
    /* FreeBSD 7.x wants this to reuse portnumbers.
     * Linux 2.6.x seems to have reserved a slot for the option, but not
     * enabled it. Survive libc's with the option on kernels without.
     *
     * The emulated Linux runtime on MS Windows 10 fails this with EINVAL.
     */
    int o=1;
    if((fd_setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *)&o, sizeof(int)) < 0)
#ifdef ENOPROTOOPT
       && (errno != ENOPROTOOPT)
#endif
#ifdef EINVAL
       && (errno != EINVAL)
#endif
#ifdef WSAENOPROTOOPT
       && (errno != WSAENOPROTOOPT)
#endif
       ){
      p->my_errno=errno;
      while (fd_close(fd) && errno == EINTR) {}
      errno = p->my_errno;
      pop_n_elems(args);
      push_int(0);
      return;
    }
  }
#endif
#ifndef __NT__
  {
    int o=1;
    if(fd_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&o, sizeof(int)) < 0)
    {
      p->my_errno=errno;
      while (fd_close(fd) && errno == EINTR) {}
      errno = p->my_errno;
      pop_n_elems(args);
      push_int(0);
      return;
    }
  }
#endif

#if defined(IPV6_V6ONLY) && defined(IPPROTO_IPV6)
  if (SOCKADDR_FAMILY(addr) == AF_INET6) {
    /* Attempt to enable dual-stack (ie mapped IPv4 adresses).
     * Needed on WIN32.
     * cf http://msdn.microsoft.com/en-us/library/windows/desktop/bb513665(v=vs.85).aspx
     */
    int o = 0;
    fd_setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&o, sizeof(int));
  }
#endif

  my_set_close_on_exec(fd,1);

  THREADS_ALLOW_UID();
  if( !(tmp=fd_bind(fd, (struct sockaddr *)&addr, addr_len) < 0) )
#ifdef TCP_FASTOPEN
      tmp = 256,
      setsockopt(fd,SOL_TCP, TCP_FASTOPEN, &tmp, sizeof(tmp)),
#endif
      (tmp =  fd_listen(fd, 16384) < 0);
  THREADS_DISALLOW_UID();

  if(!Pike_fp->current_object->prog)
  {
    if (fd >= 0)
      while (fd_close(fd) && errno == EINTR) {}
    Pike_error("Object destructed in Stdio.Port->bind()\n");
  }

  if(tmp)
  {
    p->my_errno=errno;
    while (fd_close(fd) && errno == EINTR) {}
    errno = p->my_errno;
    pop_n_elems(args);
    push_int(0);
    return;
  }

  change_fd_for_box (&p->box, fd);
  if(args > 1) assign_accept_cb (p, Pike_sp+1-args);
  p->my_errno=0;
  pop_n_elems(args);
  push_int(1);
}
Ejemplo n.º 16
0
/*! @decl int bind_unix(string path, void|function accept_callback)
 *!
 *! Opens a Unix domain socket at the given path in the file system.
 *! If the second argument is present, the socket is set to
 *! nonblocking and the callback funcition is called whenever
 *! something connects to it. The callback will receive the id for
 *! this port as argument and should typically call @[accept] to
 *! establish a connection.
 *!
 *! @returns
 *!   1 is returned on success, zero on failure. @[errno] provides
 *!   further details about the error in the latter case.
 *!
 *! @note
 *!   This function is only available on systems that support Unix domain
 *!   sockets.
 *!
 *! @note
 *!   @[path] had a quite restrictive length limit (~100 characters)
 *!   prior to Pike 7.8.334.
 *!
 *! @seealso
 *!   @[accept], @[set_id]
 */
static void bind_unix(INT32 args)
{
  struct port *p = THIS;
  struct sockaddr_un *addr;
  struct pike_string *path;
  struct svalue *cb = NULL;
  int addr_len,fd,tmp;

  do_close(p);

  get_all_args(NULL, args, "%n.%*", &path, &cb);

  /* NOTE: Some operating systems (eg Linux 2.6) do not support
   *       paths longer than what fits into a plain struct sockaddr_un.
   */
  addr_len = sizeof(struct sockaddr_un) + path->len + 1 -
    sizeof(addr->sun_path);
  addr = xalloc(addr_len);

  strcpy(addr->sun_path, path->str);
  addr->sun_family = AF_UNIX;
#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
  /* Length including NUL. */
  addr->sun_len = path->len + 1;
#endif

  fd=fd_socket(AF_UNIX, SOCK_STREAM, 0);

  if(fd < 0)
  {
    free(addr);
    p->my_errno=errno;
    pop_n_elems(args);
    push_int(0);
    return;
  }

#ifndef __NT__
  {
    int o=1;
    do {
      tmp = fd_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
			  (char *)&o, sizeof(int));
    } while ((tmp < 0) && (errno == EINTR));
  }
#endif

  my_set_close_on_exec(fd,1);

  THREADS_ALLOW_UID();
  do {
    tmp = fd_bind(fd, (struct sockaddr *)addr, addr_len);
  } while ((tmp < 0) && (errno == EINTR));
  if (tmp >= 0) {
    do {
      tmp = fd_listen(fd, 16384);
    } while ((tmp < 0) && (errno == EINTR));
  }
  THREADS_DISALLOW_UID();

  free(addr);

  if(!Pike_fp->current_object->prog)
  {
    if (fd >= 0)
      while (fd_close(fd) && errno == EINTR) {}
    Pike_error("Object destructed in Stdio.Port->bind_unix()\n");
  }

  if(tmp < 0)
  {
    p->my_errno=errno;
    while (fd_close(fd) && errno == EINTR) {}
    errno = p->my_errno;
    pop_n_elems(args);
    push_int(0);
    return;
  }

  change_fd_for_box (&p->box, fd);
  if (cb) assign_accept_cb (p, cb);
  p->my_errno=0;
  pop_n_elems(args);
  push_int(1);
}
Ejemplo n.º 17
0
/**
 * Connect to a remote peer
 *
 * @param tc   TCP Connection object
 * @param peer Network address of peer
 *
 * @return 0 if success, otherwise errorcode
 */
int tcp_conn_connect(struct tcp_conn *tc, const struct sa *peer)
{
	struct addrinfo hints, *res = NULL, *r;
	char addr[64];
	char serv[NI_MAXSERV];
	int error, err = 0;

	if (!tc || !sa_isset(peer, SA_ALL))
		return EINVAL;

	tc->active = true;

	if (tc->fdc < 0) {
		DEBUG_WARNING("invalid fd\n");
		return EBADF;
	}

	memset(&hints, 0, sizeof(hints));
	/* set-up hints structure */
	hints.ai_family   = PF_UNSPEC;
	hints.ai_flags    = AI_PASSIVE | AI_NUMERICHOST;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;

	(void)re_snprintf(addr, sizeof(addr), "%H",
			  sa_print_addr, peer);
	(void)re_snprintf(serv, sizeof(serv), "%u", sa_port(peer));

	error = getaddrinfo(addr, serv, &hints, &res);
	if (error) {
		DEBUG_WARNING("connect: getaddrinfo(): (%s)\n",
			      gai_strerror(error));
		return EADDRNOTAVAIL;
	}

	for (r = res; r; r = r->ai_next) {
		struct sockaddr *sa = r->ai_addr;

	again:
		if (0 == connect(tc->fdc, sa, SIZ_CAST r->ai_addrlen)) {
			err = 0;
			goto out;
		}
		else {
#ifdef WIN32
			/* Special error handling for Windows */
			if (WSAEWOULDBLOCK == WSAGetLastError()) {
				err = 0;
				goto out;
			}
#endif

			/* Special case for mingw32/wine */
			if (0 == errno) {
				err = 0;
				goto out;
			}

			if (EINTR == errno)
				goto again;

			if (EINPROGRESS != errno && EALREADY != errno) {
				err = errno;
				DEBUG_INFO("connect: connect() %J: %m\n",
					   peer, err);
			}
		}
	}

 out:
	freeaddrinfo(res);

	if (err)
		return err;

	return fd_listen(tc->fdc, FD_READ | FD_WRITE | FD_EXCEPT,
			 tcp_recv_handler, tc);
}
Ejemplo n.º 18
0
static void tcp_recv_handler(int flags, void *arg)
{
	struct tcp_conn *tc = arg;
	struct mbuf *mb = NULL;
	bool hlp_estab = false;
	struct le *le;
	ssize_t n;
	int err;
	socklen_t err_len = sizeof(err);

	if (flags & FD_EXCEPT) {
		DEBUG_INFO("recv handler: got FD_EXCEPT on fd=%d\n", tc->fdc);
	}

	/* check for any errors */
	if (-1 == getsockopt(tc->fdc, SOL_SOCKET, SO_ERROR,
			     BUF_CAST &err, &err_len)) {
		DEBUG_WARNING("recv handler: getsockopt: (%m)\n", errno);
		return;
	}

	if (err) {
		conn_close(tc, err);
		return;
	}
#if 0
	if (EINPROGRESS != err && EALREADY != err) {
		DEBUG_WARNING("recv handler: Socket error (%m)\n", err);
		return;
	}
#endif

	if (flags & FD_WRITE) {

		if (tc->connected) {

			uint32_t nrefs;

			mem_ref(tc);

			err = dequeue(tc);

			nrefs = mem_nrefs(tc);
			mem_deref(tc);

			/* check if connection was deref'd from send handler */
			if (nrefs == 1)
				return;

			if (err) {
				conn_close(tc, err);
				return;
			}

			if (!tc->sendq.head && !tc->sendh) {

				err = fd_listen(tc->fdc, FD_READ,
						tcp_recv_handler, tc);
				if (err) {
					conn_close(tc, err);
					return;
				}
			}

			if (flags & FD_READ)
				goto read;

			return;
		}

		tc->connected = true;

		err = fd_listen(tc->fdc, FD_READ, tcp_recv_handler, tc);
		if (err) {
			DEBUG_WARNING("recv handler: fd_listen(): %m\n", err);
			conn_close(tc, err);
			return;
		}

		le = tc->helpers.head;
		while (le) {
			struct tcp_helper *th = le->data;

			le = le->next;

			if (th->estabh(&err, tc->active, th->arg) || err) {
				if (err)
					conn_close(tc, err);
				return;
			}
		}

		if (tc->estabh)
			tc->estabh(tc->arg);

		return;
	}

 read:
	mb = mbuf_alloc(tc->rxsz);
	if (!mb)
		return;

	n = recv(tc->fdc, BUF_CAST mb->buf, mb->size, 0);
	if (0 == n) {
		mem_deref(mb);
		conn_close(tc, 0);
		return;
	}
	else if (n < 0) {
		DEBUG_WARNING("recv handler: recv(): %m\n", errno);
		goto out;
	}

	mb->end = n;

	le = tc->helpers.head;
	while (le) {
		struct tcp_helper *th = le->data;
		bool hdld = false;

		le = le->next;

		if (hlp_estab) {

			hdld |= th->estabh(&err, tc->active, th->arg);
			if (err) {
				conn_close(tc, err);
				goto out;
			}
		}

		if (mb->pos < mb->end) {

		        hdld |= th->recvh(&err, mb, &hlp_estab, th->arg);
			if (err) {
				conn_close(tc, err);
				goto out;
			}
		}

		if (hdld)
			goto out;
	}

	mbuf_trim(mb);

	if (hlp_estab && tc->estabh) {

		uint32_t nrefs;

		mem_ref(tc);

		tc->estabh(tc->arg);

		nrefs = mem_nrefs(tc);
		mem_deref(tc);

		/* check if connection was deref'ed from establish handler */
		if (nrefs == 1)
			goto out;
	}

	if (mb->pos < mb->end && tc->recvh) {
		tc->recvh(mb, tc->arg);
	}

 out:
	mem_deref(mb);
}