/** * 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; }
/** * Allocate a TCP Connection * * @param tcp Returned TCP Connection object * @param peer Network address of peer * @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_conn_alloc(struct tcp_conn **tcp, const struct sa *peer, tcp_estab_h *eh, tcp_recv_h *rh, tcp_close_h *ch, void *arg) { struct tcp_conn *tc; struct addrinfo hints, *res = NULL, *r; char addr[64]; char serv[NI_MAXSERV] = "0"; int error, err; if (!tcp || !sa_isset(peer, SA_ALL)) return EINVAL; tc = conn_alloc(eh, rh, ch, arg); if (!tc) return ENOMEM; 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)); err = EADDRNOTAVAIL; goto out; } err = EINVAL; for (r = res; r; r = r->ai_next) { tc->fdc = SOK_CAST socket(r->ai_family, SOCK_STREAM, IPPROTO_TCP); if (tc->fdc < 0) { err = errno; continue; } err = net_sockopt_blocking_set(tc->fdc, false); if (err) { DEBUG_WARNING("connect: nonblock set: %m\n", err); (void)close(tc->fdc); tc->fdc = -1; continue; } tcp_sockopt_set(tc->fdc); err = 0; break; } freeaddrinfo(res); out: if (err) mem_deref(tc); else *tcp = tc; return err; }
/** * 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); }
/** * Create a TCP Socket * * @param tsp Pointer to returned TCP Socket * @param local Local listen address (NULL for any) * @param ch Incoming connection handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int tcp_sock_alloc(struct tcp_sock **tsp, const struct sa *local, tcp_conn_h *ch, void *arg) { struct addrinfo hints, *res = NULL, *r; char addr[64] = ""; char serv[6] = "0"; struct tcp_sock *ts = NULL; int error, err; if (!tsp) return EINVAL; ts = mem_zalloc(sizeof(*ts), sock_destructor); if (!ts) return ENOMEM; ts->fd = -1; ts->fdc = -1; if (local) { (void)re_snprintf(addr, sizeof(addr), "%H", sa_print_addr, local); (void)re_snprintf(serv, sizeof(serv), "%u", sa_port(local)); } 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; error = getaddrinfo(addr[0] ? addr : NULL, serv, &hints, &res); if (error) { #ifdef WIN32 DEBUG_WARNING("listen: getaddrinfo: wsaerr=%d\n", WSAGetLastError()); #endif DEBUG_WARNING("listen: getaddrinfo: %s:%s error=%d (%s)\n", addr, serv, error, gai_strerror(error)); err = EADDRNOTAVAIL; goto out; } err = EINVAL; for (r = res; r; r = r->ai_next) { int fd = -1; if (ts->fd >= 0) continue; fd = SOK_CAST socket(r->ai_family, SOCK_STREAM, IPPROTO_TCP); if (fd < 0) { err = errno; continue; } (void)net_sockopt_reuse_set(fd, true); err = net_sockopt_blocking_set(fd, false); if (err) { DEBUG_WARNING("listen: nonblock set: %m\n", err); (void)close(fd); continue; } tcp_sockopt_set(fd); /* OK */ ts->fd = fd; err = 0; break; } freeaddrinfo(res); if (-1 == ts->fd) goto out; ts->connh = ch; ts->arg = arg; out: if (err) mem_deref(ts); else *tsp = ts; return err; }
/** * Create and listen on a UDP Socket * * @param usp Pointer to returned UDP Socket * @param local Local network address * @param rh Receive handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int udp_listen(struct udp_sock **usp, const struct sa *local, udp_recv_h *rh, void *arg) { struct addrinfo hints, *res = NULL, *r; struct udp_sock *us = NULL; char addr[NET_ADDRSTRLEN]; char serv[6] = "0"; int af, error, err = 0; if (!usp) return EINVAL; us = mem_zalloc(sizeof(*us), udp_destructor); if (!us) return ENOMEM; list_init(&us->helpers); us->fd = -1; us->fd6 = -1; if (local) { af = sa_af(local); err = sa_ntop(local, addr, sizeof(addr)); (void)re_snprintf(serv, sizeof(serv), "%u", sa_port(local)); if (err) goto out; } else { #ifdef HAVE_INET6 af = AF_UNSPEC; #else af = AF_INET; #endif } memset(&hints, 0, sizeof(hints)); /* set-up hints structure */ hints.ai_family = af; hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; error = getaddrinfo(local ? addr : NULL, serv, &hints, &res); if (error) { #ifdef WIN32 DEBUG_WARNING("listen: getaddrinfo: wsaerr=%d\n", WSAGetLastError()); #endif DEBUG_WARNING("listen: getaddrinfo: %s:%s (%s)\n", addr, serv, gai_strerror(error)); err = EADDRNOTAVAIL; goto out; } for (r = res; r; r = r->ai_next) { int fd = -1; if (us->fd > 0) continue; DEBUG_INFO("listen: for: af=%d addr=%j\n", r->ai_family, r->ai_addr); fd = SOK_CAST socket(r->ai_family, SOCK_DGRAM, IPPROTO_UDP); if (fd < 0) { err = errno; continue; } err = net_sockopt_blocking_set(fd, false); if (err) { DEBUG_WARNING("udp listen: nonblock set: %s\n", strerror(err)); (void)close(fd); continue; } if (bind(fd, r->ai_addr, SIZ_CAST r->ai_addrlen) < 0) { err = errno; DEBUG_INFO("listen: bind(): %s (%J)\n", strerror(err), local); (void)close(fd); continue; } /* Can we do both IPv4 and IPv6 on same socket? */ if (AF_INET6 == r->ai_family) { struct sa sa; int on = 1; /* assume v6only */ #if defined (IPPROTO_IPV6) && defined (IPV6_V6ONLY) socklen_t on_len = sizeof(on); if (0 != getsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on, &on_len)) { on = 1; } #endif /* Extra check for unspec addr - MAC OS X/Solaris */ if (0==sa_set_sa(&sa, r->ai_addr) && sa_is_any(&sa)) { on = 1; } DEBUG_INFO("socket %d: IPV6_V6ONLY is %d\n", fd, on); if (on) { us->fd6 = fd; continue; } } /* OK */ us->fd = fd; break; } freeaddrinfo(res); /* We must have at least one socket */ if (-1 == us->fd && -1 == us->fd6) { if (0 == err) err = EADDRNOTAVAIL; goto out; } err = udp_thread_attach(us); if (err) goto out; us->rh = rh ? rh : dummy_udp_recv_handler; us->arg = arg; us->rxsz = UDP_RXSZ_DEFAULT; out: if (err) mem_deref(us); else *usp = us; return err; }